소스 검색

BETA: Summon system [Login & Buffs] (by Nyaran)

Rumen Nikiforov 14 년 전
부모
커밋
ec1bf53228

+ 7 - 1
L2J_Server_BETA/java/com/l2jserver/Config.java

@@ -116,6 +116,7 @@ public final class Config
 	public static int PLAYER_FAKEDEATH_UP_PROTECTION;
 	public static boolean STORE_SKILL_COOLTIME;
 	public static boolean SUBCLASS_STORE_SKILL_COOLTIME;
+	public static boolean SUMMON_STORE_SKILL_COOLTIME;
 	public static boolean ALT_GAME_SHIELD_BLOCKS;
 	public static int ALT_PERFECT_SHLD_BLOCK;
 	public static boolean ALLOW_CLASS_MASTERS;
@@ -129,6 +130,8 @@ public final class Config
 	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 RESTORE_SERVITOR_ON_RECONNECT;
+	public static boolean RESTORE_PET_ON_RECONNECT;
 	public static int MAX_RUN_SPEED;
 	public static int MAX_PCRIT_RATE;
 	public static int MAX_MCRIT_RATE;
@@ -1504,6 +1507,7 @@ public final class Config
 					PLAYER_FAKEDEATH_UP_PROTECTION = Integer.parseInt(Character.getProperty("PlayerFakeDeathUpProtection", "0"));
 					STORE_SKILL_COOLTIME = Boolean.parseBoolean(Character.getProperty("StoreSkillCooltime", "true"));
 					SUBCLASS_STORE_SKILL_COOLTIME = Boolean.parseBoolean(Character.getProperty("SubclassStoreSkillCooltime", "false"));
+					SUMMON_STORE_SKILL_COOLTIME = Boolean.parseBoolean(Character.getProperty("SummonStoreSkillCooltime", "True"));
 					ALT_GAME_SHIELD_BLOCKS = Boolean.parseBoolean(Character.getProperty("AltShieldBlocks", "false"));
 					ALT_PERFECT_SHLD_BLOCK = Integer.parseInt(Character.getProperty("AltPerfectShieldBlockRate", "10"));
 					ALLOW_CLASS_MASTERS = Boolean.parseBoolean(Character.getProperty("AllowClassMasters", "False"));
@@ -1517,7 +1521,9 @@ public final class Config
 					DIVINE_SP_BOOK_NEEDED = Boolean.parseBoolean(Character.getProperty("DivineInspirationSpBookNeeded", "true"));
 					ALT_GAME_SKILL_LEARN = Boolean.parseBoolean(Character.getProperty("AltGameSkillLearn", "false"));
 					ALT_GAME_SUBCLASS_WITHOUT_QUESTS = Boolean.parseBoolean(Character.getProperty("AltSubClassWithoutQuests", "False"));
-					ALT_GAME_SUBCLASS_EVERYWHERE = Boolean.parseBoolean(Character.getProperty("AltSubclassEverywhere", "False"));
+					ALT_GAME_SUBCLASS_EVERYWHERE = Boolean.parseBoolean(Character.getProperty("AltSubclassEverywhere", "True"));
+					RESTORE_SERVITOR_ON_RECONNECT = Boolean.parseBoolean(Character.getProperty("RestoreServitorOnReconnect", "true"));
+					RESTORE_PET_ON_RECONNECT = Boolean.parseBoolean(Character.getProperty("RestorePetOnReconnect", "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"));

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

@@ -39,6 +39,7 @@ import com.l2jserver.gameserver.datatables.AdminCommandAccessRights;
 import com.l2jserver.gameserver.datatables.ArmorSetsTable;
 import com.l2jserver.gameserver.datatables.AugmentationData;
 import com.l2jserver.gameserver.datatables.CharNameTable;
+import com.l2jserver.gameserver.datatables.CharSummonTable;
 import com.l2jserver.gameserver.datatables.CharTemplateTable;
 import com.l2jserver.gameserver.datatables.ClanTable;
 import com.l2jserver.gameserver.datatables.DoorTable;
@@ -249,6 +250,7 @@ public class GameServer
 		GmListTable.getInstance();
 		RaidBossPointsManager.getInstance();
 		PetDataTable.getInstance();
+		CharSummonTable.getInstance().init();
 		
 		printSection("Clans");
 		ClanTable.getInstance();

+ 327 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/CharSummonTable.java

@@ -0,0 +1,327 @@
+/*
+ * 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.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.l2jserver.Config;
+import com.l2jserver.L2DatabaseFactory;
+import com.l2jserver.gameserver.idfactory.IdFactory;
+import com.l2jserver.gameserver.model.L2ItemInstance;
+import com.l2jserver.gameserver.model.L2SummonItem;
+import com.l2jserver.gameserver.model.actor.instance.L2MerchantSummonInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2PetInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2SiegeSummonInstance;
+import com.l2jserver.gameserver.model.actor.instance.L2SummonInstance;
+import com.l2jserver.gameserver.model.base.Experience;
+import com.l2jserver.gameserver.network.serverpackets.PetItemList;
+import com.l2jserver.gameserver.skills.l2skills.L2SkillSummon;
+import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
+
+/**
+ * @author Nyaran
+ */
+public class CharSummonTable
+{
+	private static Logger _log = Logger.getLogger(CharSummonTable.class.getName());
+	
+	private static final String INIT_SUMMONS = "SELECT ownerId, summonSkillId FROM character_summons";
+	private static final String INIT_PET = "SELECT ownerId, item_obj_id FROM pets WHERE restore = 'true'";
+
+	private static final String SAVE_SUMMON = "REPLACE INTO character_summons (ownerId,summonSkillId,curHp,curMp,time) VALUES (?,?,?,?,?)";
+	private static final String LOAD_SUMMON = "SELECT curHp, curMp, time FROM character_summons WHERE ownerId = ? AND summonSkillId = ?";
+	private static final String REMOVE_SUMMON = "DELETE FROM character_summons WHERE ownerId = ?";
+	
+	private static final TIntIntHashMap _servitors = new TIntIntHashMap();
+	private static final TIntIntHashMap _pets = new TIntIntHashMap();
+	
+	public static CharSummonTable getInstance()
+	{
+		return SingletonHolder._instance;
+	}
+	
+	public void init()
+	{
+		int ownerId;
+		int refId;
+		
+		Connection con = null;
+		
+		if (Config.RESTORE_SERVITOR_ON_RECONNECT)
+		{
+			try
+			{
+				con = L2DatabaseFactory.getInstance().getConnection();
+				PreparedStatement statement = con.prepareStatement(INIT_SUMMONS);
+				ResultSet rset = statement.executeQuery();
+				
+				while (rset.next())
+				{
+					ownerId = rset.getInt("ownerId");
+					refId = rset.getInt("summonSkillId");
+					
+					_servitors.put(ownerId, refId);
+				}
+				
+				rset.close();
+				statement.close();
+			}
+			catch (Exception e)
+			{
+				_log.log(Level.SEVERE, "Error while loading saved summons", e);
+			}
+			finally
+			{
+				L2DatabaseFactory.close(con);
+			}
+		}
+		
+		if (Config.RESTORE_PET_ON_RECONNECT)
+		{
+			try
+			{
+				con = L2DatabaseFactory.getInstance().getConnection();
+				PreparedStatement statement = con.prepareStatement(INIT_PET);
+				ResultSet rset = statement.executeQuery();
+				
+				while (rset.next())
+				{
+					ownerId = rset.getInt("ownerId");
+					refId = rset.getInt("item_obj_id");
+					
+					_pets.put(ownerId, refId);
+				}
+				
+				rset.close();
+				statement.close();
+			}
+			
+			catch (Exception e)
+			{
+				_log.log(Level.SEVERE, "Error while loading saved summons", e);
+			}
+			finally
+			{
+				L2DatabaseFactory.close(con);
+			}
+		}
+	}
+	
+	public TIntIntHashMap getServitors()
+	{
+		return _servitors;
+	}
+	
+	public TIntIntHashMap getPets()
+	{
+		return _pets;
+	}
+	
+	public void saveSummon(L2SummonInstance summon)
+	{
+		Connection con = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+			PreparedStatement statement = con.prepareStatement(SAVE_SUMMON);
+			
+			statement.setInt(1, summon.getOwner().getObjectId());
+			statement.setInt(2, summon.getReferenceSkill());
+			statement.setDouble(3, summon.getCurrentHp());
+			statement.setDouble(4, summon.getCurrentMp());
+			statement.setInt(5, summon.getTimeRemaining());
+			
+			statement.execute();
+			statement.close();
+			
+			_servitors.put(summon.getOwner().getObjectId(), summon.getReferenceSkill());
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "Failed to store summon [SummonId: " + summon.getNpcId() + "] from Char [CharId: " + summon.getOwner().getObjectId() + "] data", e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+		}
+	}
+	
+	public void restoreServitor(L2PcInstance activeChar)
+	{
+		Connection con = null;
+		try
+		{
+			int skillId = _servitors.get(activeChar.getObjectId());
+			
+			con = L2DatabaseFactory.getInstance().getConnection();
+			PreparedStatement statement = con.prepareStatement(LOAD_SUMMON);
+			statement.setInt(1, activeChar.getObjectId());
+			statement.setInt(2, skillId);
+			ResultSet rset = statement.executeQuery();
+			
+			L2NpcTemplate summonTemplate;
+			L2SummonInstance summon;
+			L2SkillSummon skill;
+			
+			while (rset.next())
+			{
+				int curHp = rset.getInt("curHp");
+				int curMp = rset.getInt("curMp");
+				int time = rset.getInt("time");
+				
+				skill = (L2SkillSummon) SkillTable.getInstance().getInfo(skillId, activeChar.getSkillLevel(skillId));
+				if (skill == null)
+				{
+					removeServitor(activeChar);
+					return;
+				}
+				
+				summonTemplate = NpcTable.getInstance().getTemplate(skill.getNpcId());
+				if (summonTemplate == null)
+				{
+					_log.warning("[CharSummonTable] Summon attemp for nonexisting Skill ID:" + skillId);
+					return;
+				}
+				
+				if (summonTemplate.type.equalsIgnoreCase("L2SiegeSummon"))
+					summon = new L2SiegeSummonInstance(IdFactory.getInstance().getNextId(), summonTemplate, activeChar, skill);
+				/* TODO: Confirm L2Merchant 
+				else if (summonTemplate.type.equalsIgnoreCase("L2MerchantSummon"))
+					summon = new L2MerchantSummonInstance(IdFactory.getInstance().getNextId(), summonTemplate, activeChar, skill);*/
+				else
+					summon = new L2SummonInstance(IdFactory.getInstance().getNextId(), summonTemplate, activeChar, skill);
+				
+				summon.setName(summonTemplate.name);
+				summon.setTitle(activeChar.getName());
+				summon.setExpPenalty(skill.getExpPenalty());
+				
+				if (summon.getLevel() >= Experience.PET_MAX_LEVEL)
+				{
+					summon.getStat().setExp(Experience.LEVEL[Experience.PET_MAX_LEVEL - 1]);
+					_log.warning("Summon (" + summon.getName() + ") NpcID: " + summon.getNpcId() + " has a level above 86. Please rectify.");
+				}
+				else
+				{
+					summon.getStat().setExp(Experience.LEVEL[(summon.getLevel() % Experience.PET_MAX_LEVEL)]);
+				}
+				summon.setCurrentHp(curHp);
+				summon.setCurrentMp(curMp);
+				summon.setHeading(activeChar.getHeading());
+				summon.setRunning();
+				if (!(summon instanceof L2MerchantSummonInstance))
+					activeChar.setPet(summon);
+				
+				summon.setTimeRemaining(time);
+				
+				//L2World.getInstance().storeObject(summon);
+				summon.spawnMe(activeChar.getX() + 20, activeChar.getY() + 20, activeChar.getZ());
+			}
+			
+			rset.close();
+			statement.close();
+		}
+		catch (SQLException e)
+		{
+			_log.log(Level.WARNING, "[CharSummonTable]: Summon cannot be restored: ", e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+		}
+	}
+	
+	public void removeServitor(L2PcInstance activeChar)
+	{
+		Connection con = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+			PreparedStatement statement = con.prepareStatement(REMOVE_SUMMON);
+			statement.setInt(1, activeChar.getObjectId());
+			
+			statement.execute();
+			statement.close();
+			_servitors.remove(activeChar.getObjectId());
+		}
+		catch (SQLException e)
+		{
+			_log.log(Level.WARNING, "[CharSummonTable]: Summon cannot be removed: ", e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+		}
+	}
+	
+	public void restorePet(L2PcInstance activeChar)
+	{
+		L2ItemInstance item = activeChar.getInventory().getItemByObjectId(_pets.get(activeChar.getObjectId()));
+		final L2SummonItem sitem = SummonItemsData.getInstance().getSummonItem(item.getItemId());
+		L2NpcTemplate npcTemplate = NpcTable.getInstance().getTemplate(sitem.getNpcId());
+		
+		if (npcTemplate == null)
+			return;
+		
+		final L2PetInstance petSummon = L2PetInstance.spawnPet(npcTemplate, activeChar, item);
+		if (petSummon == null)
+			return;
+		
+		petSummon.setShowSummonAnimation(true);
+		petSummon.setTitle(activeChar.getName());
+		
+		if (!petSummon.isRespawned())
+		{
+			petSummon.setCurrentHp(petSummon.getMaxHp());
+			petSummon.setCurrentMp(petSummon.getMaxMp());
+			petSummon.getStat().setExp(petSummon.getExpForThisLevel());
+			petSummon.setCurrentFed(petSummon.getMaxFed());
+		}
+		
+		petSummon.setRunning();
+		
+		if (!petSummon.isRespawned())
+			petSummon.store();
+		
+		activeChar.setPet(petSummon);
+		
+		petSummon.spawnMe(activeChar.getX() + 50, activeChar.getY() + 100, activeChar.getZ());
+		petSummon.startFeed();
+		item.setEnchantLevel(petSummon.getLevel());
+		
+		if (petSummon.getCurrentFed() <= 0)
+			petSummon.unSummon(activeChar);
+		else
+			petSummon.startFeed();
+		
+		petSummon.setFollowStatus(true);
+		
+		petSummon.getOwner().sendPacket(new PetItemList(petSummon));
+		petSummon.broadcastStatusUpdate();
+	}
+	
+	@SuppressWarnings("synthetic-access")
+	private static class SingletonHolder
+	{
+		protected static final CharSummonTable _instance = new CharSummonTable();
+	}
+}

+ 98 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/datatables/SummonEffectsTable.java

@@ -0,0 +1,98 @@
+/*
+ * 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.util.List;
+
+import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+
+/**
+ * @author Nyaran
+ */
+public class SummonEffectsTable
+{
+	/** Servitors **/
+	/*
+	 * Map tree
+	 * key: charObjectId, value: classIndex Map
+	 * 		key: classIndex, value: servitors Map
+	 * 			key: servitorSkillId, value: Effects list
+	 */
+	private TIntObjectHashMap<TIntObjectHashMap<TIntObjectHashMap<List<SummonEffect>>>> _servitorEffects = new TIntObjectHashMap<TIntObjectHashMap<TIntObjectHashMap<List<SummonEffect>>>>();
+
+	public TIntObjectHashMap<TIntObjectHashMap<TIntObjectHashMap<List<SummonEffect>>>> getServitorEffectsOwner()
+	{
+		return _servitorEffects;
+	}
+	
+	public TIntObjectHashMap<List<SummonEffect>> getServitorEffects(L2PcInstance owner)
+	{
+		return _servitorEffects.get(owner.getObjectId()).get(owner.getClassIndex());
+	}
+	
+	
+	/** Pets **/
+	private TIntObjectHashMap<List<SummonEffect>> _petEffects = new TIntObjectHashMap<List<SummonEffect>>(); // key: petItemObjectId, value: Effects list
+	
+	public TIntObjectHashMap<List<SummonEffect>> getPetEffects()
+	{
+		return _petEffects;
+	}
+	
+	
+	/** Common **/
+	public static SummonEffectsTable getInstance()
+	{
+		return SingletonHolder._instance;
+	}
+	
+	public class SummonEffect
+	{
+		L2Skill _skill;
+		int _effectCount;
+		int _effectCurTime;
+		
+		public SummonEffect(L2Skill skill, int effectCount, int effectCurTime)
+		{
+			_skill = skill;
+			_effectCount = effectCount;
+			_effectCurTime = effectCurTime;
+		}
+		
+		public L2Skill getSkill()
+		{
+			return _skill;
+		}
+		
+		public int getEffectCount()
+		{
+			return _effectCount;
+		}
+		
+		public int getEffectCurTime()
+		{
+			return _effectCurTime;
+		}
+	}
+	
+	@SuppressWarnings("synthetic-access")
+	private static class SingletonHolder
+	{
+		protected static final SummonEffectsTable _instance = new SummonEffectsTable();
+	}
+}

+ 27 - 2
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Summon.java

@@ -16,6 +16,7 @@ package com.l2jserver.gameserver.model.actor;
 
 import java.util.Collection;
 
+import com.l2jserver.Config;
 import com.l2jserver.gameserver.ai.CtrlIntention;
 import com.l2jserver.gameserver.ai.L2CharacterAI;
 import com.l2jserver.gameserver.ai.L2SummonAI;
@@ -73,6 +74,8 @@ public abstract class L2Summon extends L2Playable
 	// we dont have walk speed in pet data so for now use runspd / 3
 	public static final int WALK_SPEED_MULTIPLIER = 3;
 	
+	public boolean _restoreSummon = true;
+	
 	public class AIAccessor extends L2Character.AIAccessor
 	{
 		protected AIAccessor() {}
@@ -105,6 +108,9 @@ public abstract class L2Summon extends L2Playable
 		super.onSpawn();
 		if (!(this instanceof L2MerchantSummonInstance))
 		{
+			if (Config.SUMMON_STORE_SKILL_COOLTIME)
+				restoreEffects();
+			
 			this.setFollowStatus(true);
 			updateAndBroadcastStatus(0);
 			getOwner().sendPacket(new RelationChanged(this, getOwner().getRelation(getOwner()), false));
@@ -118,7 +124,7 @@ public abstract class L2Summon extends L2Playable
 		}
 		setShowSummonAnimation(false); // addVisibleObject created the info packets with summon animation
 		// if someone comes into range now, the animation shouldnt show any more
-		
+		_restoreSummon = false;
 	}
 	
 	@Override
@@ -296,6 +302,12 @@ public abstract class L2Summon extends L2Playable
 	@Override
 	public boolean doDie(L2Character killer)
 	{
+		if (isNoblesseBlessed())
+		{
+			stopNoblesseBlessing(null);
+			storeEffect(true);
+		}
+		
 		if (!super.doDie(killer))
 			return false;
 		if (this instanceof L2MerchantSummonInstance)
@@ -391,7 +403,8 @@ public abstract class L2Summon extends L2Playable
 			else
 				getOwner().setPetInvItems(false);
 			
-			store();			
+			store();
+			storeEffect(true);
 			owner.setPet(null);
 			
 			// Stop AI tasks
@@ -476,10 +489,22 @@ public abstract class L2Summon extends L2Playable
 	{
 	}
 	
+	public void setRestoreSummon(boolean val)
+	{
+	}
+	
 	public void store()
 	{
 	}
 	
+	public void storeEffect(boolean storeEffects)
+	{
+	}
+	
+	public void restoreEffects()
+	{
+	}
+	
 	@Override
 	public L2ItemInstance getActiveWeaponInstance()
 	{

+ 15 - 5
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java

@@ -60,6 +60,7 @@ import com.l2jserver.gameserver.communitybbs.Manager.RegionBBSManager;
 import com.l2jserver.gameserver.datatables.AccessLevels;
 import com.l2jserver.gameserver.datatables.AdminCommandAccessRights;
 import com.l2jserver.gameserver.datatables.CharNameTable;
+import com.l2jserver.gameserver.datatables.CharSummonTable;
 import com.l2jserver.gameserver.datatables.CharTemplateTable;
 import com.l2jserver.gameserver.datatables.ClanTable;
 import com.l2jserver.gameserver.datatables.EnchantGroupsTable;
@@ -4199,7 +4200,7 @@ public final class L2PcInstance extends L2Playable
 			}
 			case SUMMON:
 			{
-				if (!((L2SkillSummon)skill).isCubic() && (getPet() != null || isMounted()))
+				if (!((L2SkillSummon)skill).isCubic() && (getPet() != null || isMounted() || CharSummonTable.getInstance().getPets().contains(getObjectId()) || CharSummonTable.getInstance().getPets().contains(getObjectId())))
 				{
 					if (Config.DEBUG)
 						_log.fine("player has a pet already. ignore summon skill");
@@ -10733,6 +10734,9 @@ public final class L2PcInstance extends L2Playable
 			_charges.set(0);
 			stopChargeTask();
 			
+			if (getPet() instanceof L2SummonInstance)
+				getPet().unSummon(this);
+			
 			if (classIndex == 0)
 			{
 				setClassTemplate(getBaseClass());
@@ -10766,12 +10770,8 @@ public final class L2PcInstance extends L2Playable
 			 * 7. Reset HP/MP/CP stats and send Server->Client character status packet to reflect changes.
 			 * 8. Restore shortcut data related to this class.
 			 * 9. Resend a class change animation effect to broadcast to all nearby players.
-			 * 10.Unsummon any active servitor from the player.
 			 */
 			
-			if (getPet() instanceof L2SummonInstance)
-				getPet().unSummon(this);
-			
 			for (L2Skill oldSkill : getAllSkills())
 				super.removeSkill(oldSkill);
 			
@@ -11099,7 +11099,14 @@ public final class L2PcInstance extends L2Playable
 	public void onActionRequest()
 	{
 		if (isSpawnProtected())
+		{
 			sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_ARE_NO_LONGER_PROTECTED_FROM_AGGRESSIVE_MONSTERS));
+			
+			if (Config.RESTORE_SERVITOR_ON_RECONNECT && getPet() == null && CharSummonTable.getInstance().getServitors().containsKey(getObjectId()))
+				CharSummonTable.getInstance().restoreServitor(this);
+			if (Config.RESTORE_PET_ON_RECONNECT && getPet() == null && CharSummonTable.getInstance().getPets().containsKey(getObjectId()))
+				CharSummonTable.getInstance().restorePet(this);
+		}
 		if (isTeleportProtected())
 			sendMessage("Teleport spawn protection ended.");
 		setProtection(false);
@@ -11739,6 +11746,9 @@ public final class L2PcInstance extends L2Playable
 		{
 			try
 			{
+				
+				getPet().setRestoreSummon(true);
+				
 				getPet().unSummon(this);
 				// dead pet wasnt unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline)
 				if (getPet() != null)

+ 185 - 4
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2PetInstance.java

@@ -17,6 +17,7 @@ package com.l2jserver.gameserver.model.actor.instance;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.util.List;
 import java.util.concurrent.Future;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -27,14 +28,18 @@ import com.l2jserver.Config;
 import com.l2jserver.L2DatabaseFactory;
 import com.l2jserver.gameserver.ThreadPoolManager;
 import com.l2jserver.gameserver.ai.CtrlIntention;
+import com.l2jserver.gameserver.datatables.CharSummonTable;
 import com.l2jserver.gameserver.datatables.ItemTable;
 import com.l2jserver.gameserver.datatables.PetDataTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.datatables.SummonEffectsTable;
+import com.l2jserver.gameserver.datatables.SummonEffectsTable.SummonEffect;
 import com.l2jserver.gameserver.handler.IItemHandler;
 import com.l2jserver.gameserver.handler.ItemHandler;
 import com.l2jserver.gameserver.idfactory.IdFactory;
 import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
 import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager;
+import com.l2jserver.gameserver.model.L2Effect;
 import com.l2jserver.gameserver.model.L2ItemInstance;
 import com.l2jserver.gameserver.model.L2Object;
 import com.l2jserver.gameserver.model.L2PetData;
@@ -54,9 +59,11 @@ import com.l2jserver.gameserver.network.serverpackets.PetItemList;
 import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
 import com.l2jserver.gameserver.network.serverpackets.StopMove;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
+import com.l2jserver.gameserver.skills.Env;
 import com.l2jserver.gameserver.skills.Stats;
 import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
+import com.l2jserver.gameserver.templates.effects.EffectTemplate;
 import com.l2jserver.gameserver.templates.item.L2EtcItemType;
 import com.l2jserver.gameserver.templates.item.L2Item;
 import com.l2jserver.gameserver.templates.item.L2Weapon;
@@ -74,6 +81,10 @@ public class L2PetInstance extends L2Summon
 {
 	protected static final Logger _logPet = Logger.getLogger(L2PetInstance.class.getName());
 	
+	private static final String ADD_SKILL_SAVE = "INSERT INTO character_pet_skills_save (petObjItemId,skill_id,skill_level,effect_count,effect_cur_time,buff_index) VALUES (?,?,?,?,?,?)";
+	private static final String RESTORE_SKILL_SAVE = "SELECT petObjItemId,skill_id,skill_level,effect_count,effect_cur_time,buff_index FROM character_pet_skills_save WHERE petObjItemId=? ORDER BY buff_index ASC";
+	private static final String DELETE_SKILL_SAVE = "DELETE FROM character_pet_skills_save WHERE petObjItemId=?";
+
 	private int _curFed;
 	private PetInventory _inventory;
 	private final int _controlObjectId;
@@ -854,6 +865,12 @@ public class L2PetInstance extends L2Summon
 		}
 	}
 	
+	@Override
+	public void setRestoreSummon(boolean val)
+	{
+		_restoreSummon = val;
+	}
+	
 	@Override
 	public void store()
 	{
@@ -863,12 +880,15 @@ public class L2PetInstance extends L2Summon
 			return;
 		}
 		
+		if (!Config.RESTORE_PET_ON_RECONNECT)
+			_restoreSummon = false;
+		
 		String req;
 		if (!isRespawned())
-			req = "INSERT INTO pets (name,level,curHp,curMp,exp,sp,fed,item_obj_id) "+
-			"VALUES (?,?,?,?,?,?,?,?)";
+			req = "INSERT INTO pets (name,level,curHp,curMp,exp,sp,fed,ownerId,restore,item_obj_id) "+
+			"VALUES (?,?,?,?,?,?,?,?,?,?)";
 		else
-			req = "UPDATE pets SET name=?,level=?,curHp=?,curMp=?,exp=?,sp=?,fed=? "+
+			req = "UPDATE pets SET name=?,level=?,curHp=?,curMp=?,exp=?,sp=?,fed=?,ownerId=?,restore=? "+
 			"WHERE item_obj_id = ?";
 		Connection con = null;
 		try
@@ -882,10 +902,18 @@ public class L2PetInstance extends L2Summon
 			statement.setLong(5, getStat().getExp());
 			statement.setInt(6, getStat().getSp());
 			statement.setInt(7, getCurrentFed());
-			statement.setInt(8, getControlObjectId());
+			statement.setInt(8, getOwner().getObjectId());
+			statement.setString(9, String.valueOf(_restoreSummon)); // True restores pet on login
+			statement.setInt(10, getControlObjectId());
+			
 			statement.executeUpdate();
 			statement.close();
 			_respawned = true;
+			
+			if (_restoreSummon)
+				CharSummonTable.getInstance().getPets().put(getOwner().getObjectId(), getControlObjectId());
+			else
+				CharSummonTable.getInstance().getPets().remove(getOwner().getObjectId());
 		}
 		catch (Exception e)
 		{
@@ -904,6 +932,159 @@ public class L2PetInstance extends L2Summon
 		}
 	}
 	
+	@Override
+	public void storeEffect(boolean storeEffects)
+	{
+		if (!Config.SUMMON_STORE_SKILL_COOLTIME)
+			return;
+		
+		// Clear list for overwrite
+		if (SummonEffectsTable.getInstance().getPetEffects().contains(getControlObjectId()))
+			SummonEffectsTable.getInstance().getPetEffects().get(getControlObjectId()).clear();
+		
+		Connection con = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+			
+			// Delete all current stored effects for summon to avoid dupe
+			PreparedStatement statement = con.prepareStatement(DELETE_SKILL_SAVE);
+			
+			statement.setInt(1, getControlObjectId());
+			statement.execute();
+			statement.close();
+			
+			int buff_index = 0;
+			
+			final List<Integer> storedSkills = new FastList<Integer>();
+			
+			//Store all effect data along with calculated remaining
+			statement = con.prepareStatement(ADD_SKILL_SAVE);
+			
+			if (storeEffects)
+			{
+				for (L2Effect effect : getAllEffects())
+				{
+					if (effect == null)
+						continue;
+					
+					switch (effect.getEffectType())
+					{
+						case HEAL_OVER_TIME:
+						case COMBAT_POINT_HEAL_OVER_TIME:
+							// TODO: Fix me.
+						case HIDE:
+							continue;
+					}
+					
+					L2Skill skill = effect.getSkill();
+					if (storedSkills.contains(skill.getReuseHashCode()))
+						continue;
+					
+					storedSkills.add(skill.getReuseHashCode());
+					
+					if (!effect.isHerbEffect() && effect.getInUse() && !skill.isToggle())
+					{
+						statement.setInt(1, getControlObjectId());
+						statement.setInt(2, skill.getId());
+						statement.setInt(3, skill.getLevel());
+						statement.setInt(4, effect.getCount());
+						statement.setInt(5, effect.getTime());
+						statement.setInt(6, ++buff_index);
+						statement.execute();
+						
+						if (!SummonEffectsTable.getInstance().getPetEffects().contains(getControlObjectId()))
+							SummonEffectsTable.getInstance().getPetEffects().put(getControlObjectId(), new FastList<SummonEffect>());
+
+						SummonEffectsTable.getInstance().getPetEffects().get(getControlObjectId()).add(SummonEffectsTable.getInstance().new SummonEffect(skill, effect.getCount(), effect.getTime()));
+					}
+				}
+				statement.close();
+			}
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.WARNING, "Could not store pet effect data: ", e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+		}
+	}
+	
+	@Override
+	public void restoreEffects()
+	{
+		Connection con = null;
+		PreparedStatement statement = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+
+			if (!SummonEffectsTable.getInstance().getPetEffects().contains(getControlObjectId()))
+			{
+				statement = con.prepareStatement(RESTORE_SKILL_SAVE);
+				statement.setInt(1, getControlObjectId());
+				ResultSet rset = statement.executeQuery();
+				
+				while (rset.next())
+				{
+					int effectCount = rset.getInt("effect_count");
+					int effectCurTime = rset.getInt("effect_cur_time");
+					
+					final L2Skill skill = SkillTable.getInstance().getInfo(rset.getInt("skill_id"), rset.getInt("skill_level"));
+					if (skill == null)
+						continue;
+					
+					if (skill.hasEffects())
+					{
+						if (!SummonEffectsTable.getInstance().getPetEffects().contains(getControlObjectId()))
+							SummonEffectsTable.getInstance().getPetEffects().put(getControlObjectId(), new FastList<SummonEffect>());
+						
+						SummonEffectsTable.getInstance().getPetEffects().get(getControlObjectId()).add(SummonEffectsTable.getInstance().new SummonEffect(skill, effectCount, effectCurTime));
+					}
+				}
+				
+				rset.close();
+				statement.close();
+			}
+			statement = con.prepareStatement(DELETE_SKILL_SAVE);
+			statement.setInt(1, getControlObjectId());
+			statement.executeUpdate();
+			statement.close();
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.WARNING, "Could not restore " + this + " active effect data: " + e.getMessage(), e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+			
+			if (SummonEffectsTable.getInstance().getPetEffects().get(getControlObjectId()) == null)
+				return;
+
+			for (SummonEffect se : SummonEffectsTable.getInstance().getPetEffects().get(getControlObjectId()))
+			{
+				Env env = new Env();
+				env.player = this;
+				env.target = this;
+				env.skill = se.getSkill();
+				L2Effect ef;
+				for (EffectTemplate et : se.getSkill().getEffectTemplates())
+				{
+					ef = et.getEffect(env);
+					if (ef != null)
+					{
+						ef.setCount(se.getEffectCount());
+						ef.setFirstTime(se.getEffectCurTime());
+						ef.scheduleEffect();
+					}
+				}
+			}
+		}
+	}
+	
 	public synchronized void stopFeed()
 	{
 		if (_feedTask != null)

+ 242 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2SummonInstance.java

@@ -14,26 +14,45 @@
  */
 package com.l2jserver.gameserver.model.actor.instance;
 
+import gnu.trove.TIntObjectHashMap;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.List;
 import java.util.concurrent.Future;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import javolution.util.FastList;
+
 import com.l2jserver.Config;
+import com.l2jserver.L2DatabaseFactory;
 import com.l2jserver.gameserver.ThreadPoolManager;
+import com.l2jserver.gameserver.datatables.CharSummonTable;
 import com.l2jserver.gameserver.datatables.SkillTable;
+import com.l2jserver.gameserver.datatables.SummonEffectsTable;
+import com.l2jserver.gameserver.datatables.SummonEffectsTable.SummonEffect;
+import com.l2jserver.gameserver.model.L2Effect;
 import com.l2jserver.gameserver.model.L2Object;
 import com.l2jserver.gameserver.model.L2Skill;
 import com.l2jserver.gameserver.model.actor.L2Character;
 import com.l2jserver.gameserver.model.actor.L2Summon;
 import com.l2jserver.gameserver.network.serverpackets.SetSummonRemainTime;
+import com.l2jserver.gameserver.skills.Env;
 import com.l2jserver.gameserver.skills.l2skills.L2SkillSummon;
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
+import com.l2jserver.gameserver.templates.effects.EffectTemplate;
 
 
 public class L2SummonInstance extends L2Summon
 {
 	protected static final Logger log = Logger.getLogger(L2SummonInstance.class.getName());
 	
+	private static final String ADD_SKILL_SAVE = "INSERT INTO character_summon_skills_save (ownerId,ownerClassIndex,summonSkillId,skill_id,skill_level,effect_count,effect_cur_time,buff_index) VALUES (?,?,?,?,?,?,?,?)";
+	private static final String RESTORE_SKILL_SAVE = "SELECT skill_id,skill_level,effect_count,effect_cur_time,buff_index FROM character_summon_skills_save WHERE ownerId=? AND ownerClassIndex=? AND summonSkillId=? ORDER BY buff_index ASC";
+	private static final String DELETE_SKILL_SAVE = "DELETE FROM character_summon_skills_save WHERE ownerId=? AND ownerClassIndex=? AND summonSkillId=?";
+
 	private float _expPenalty = 0; // exp decrease multiplier (i.e. 0.3 (= 30%) for shadow)
 	private int _itemConsumeId;
 	private int _itemConsumeCount;
@@ -47,6 +66,8 @@ public class L2SummonInstance extends L2Summon
 	
 	private Future<?> _summonLifeTask;
 	
+	private int _referenceSkill;
+	
 	public L2SummonInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2Skill skill)
 	{
 		super(objectId, template, owner);
@@ -62,6 +83,7 @@ public class L2SummonInstance extends L2Summon
 			_totalLifeTime = summonSkill.getTotalLifeTime();
 			_timeLostIdle = summonSkill.getTimeLostIdle();
 			_timeLostActive = summonSkill.getTimeLostActive();
+			_referenceSkill = summonSkill.getId();
 		}
 		else
 		{
@@ -191,6 +213,9 @@ public class L2SummonInstance extends L2Summon
 			_summonLifeTask.cancel(false);
 			_summonLifeTask = null;
 		}
+		
+		CharSummonTable.getInstance().removeServitor(getOwner());
+		
 		return true;
 		
 	}
@@ -223,6 +248,210 @@ public class L2SummonInstance extends L2Summon
 			super.doCast(skill);
 	}
 	
+	@Override
+	public void setRestoreSummon(boolean val)
+	{
+		_restoreSummon = val;
+	}
+	
+	@Override
+	public void store()
+	{
+		if (_referenceSkill == 0 || isDead())
+			return;
+		
+		if (Config.RESTORE_SERVITOR_ON_RECONNECT)
+			CharSummonTable.getInstance().saveSummon(this);
+	}
+	
+	@Override
+	public void storeEffect(boolean storeEffects)
+	{
+		if (!Config.SUMMON_STORE_SKILL_COOLTIME)
+			return;
+		
+		if (getOwner().isInOlympiadMode())
+			return;
+		
+		// Clear list for overwrite
+		if (
+				SummonEffectsTable.getInstance().getServitorEffectsOwner().contains(getOwner().getObjectId()) &&
+				SummonEffectsTable.getInstance().getServitorEffectsOwner().get(getOwner().getObjectId()).contains(getOwner().getClassIndex()) &&
+				SummonEffectsTable.getInstance().getServitorEffects(getOwner()).contains(getReferenceSkill())
+			)
+			SummonEffectsTable.getInstance().getServitorEffects(getOwner()).get(getReferenceSkill()).clear();
+		
+		Connection con = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+			
+			// Delete all current stored effects for summon to avoid dupe
+			PreparedStatement statement = con.prepareStatement(DELETE_SKILL_SAVE);
+			
+			statement.setInt(1, getOwner().getObjectId());
+			statement.setInt(2, getOwner().getClassIndex());
+			statement.setInt(3, getReferenceSkill());
+			
+			statement.execute();
+			statement.close();
+			
+			int buff_index = 0;
+			
+			final List<Integer> storedSkills = new FastList<Integer>();
+			
+			//Store all effect data along with calculated remaining
+			statement = con.prepareStatement(ADD_SKILL_SAVE);
+			
+			if (storeEffects)
+			{
+				for (L2Effect effect : getAllEffects())
+				{
+					if (effect == null)
+						continue;
+					
+					switch (effect.getEffectType())
+					{
+						case HEAL_OVER_TIME:
+						case COMBAT_POINT_HEAL_OVER_TIME:
+							// TODO: Fix me.
+						case HIDE:
+							continue;
+					}
+					
+					L2Skill skill = effect.getSkill();
+					if (storedSkills.contains(skill.getReuseHashCode()))
+						continue;
+					
+					storedSkills.add(skill.getReuseHashCode());
+					
+					if (!effect.isHerbEffect() && effect.getInUse() && !skill.isToggle())
+					{
+						statement.setInt(1, getOwner().getObjectId());
+						statement.setInt(2, getOwner().getClassIndex());
+						statement.setInt(3, getReferenceSkill());
+						statement.setInt(4, skill.getId());
+						statement.setInt(5, skill.getLevel());
+						statement.setInt(6, effect.getCount());
+						statement.setInt(7, effect.getTime());
+						statement.setInt(8, ++buff_index);
+						statement.execute();
+						
+						if (!SummonEffectsTable.getInstance().getServitorEffectsOwner().contains(getOwner().getObjectId())) // Check if charId exists in map
+							SummonEffectsTable.getInstance().getServitorEffectsOwner().put(getOwner().getObjectId(), new TIntObjectHashMap<TIntObjectHashMap<List<SummonEffect>>>());
+						if (!SummonEffectsTable.getInstance().getServitorEffectsOwner().get(getOwner().getObjectId()).contains(getOwner().getClassIndex())) // Check if classIndex exists in charId map
+							SummonEffectsTable.getInstance().getServitorEffectsOwner().get(getOwner().getObjectId()).put(getOwner().getClassIndex(), new TIntObjectHashMap<List<SummonEffect>>());
+						if (!SummonEffectsTable.getInstance().getServitorEffects(getOwner()).contains(getReferenceSkill())) // Check is summonSkillId exists in charId+classIndex map
+							SummonEffectsTable.getInstance().getServitorEffects(getOwner()).put(getReferenceSkill(), new FastList<SummonEffect>());
+
+						SummonEffectsTable.getInstance().getServitorEffects(getOwner()).get(getReferenceSkill()).add(SummonEffectsTable.getInstance().new SummonEffect(skill, effect.getCount(), effect.getTime()));
+
+					}
+				}
+				statement.close();
+			}
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.WARNING, "Could not store summon effect data: ", e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+		}
+	}
+	
+	@Override
+	public void restoreEffects()
+	{
+		if (getOwner().isInOlympiadMode())
+			return;
+		
+		Connection con = null;
+		PreparedStatement statement = null;
+		try
+		{
+			con = L2DatabaseFactory.getInstance().getConnection();
+			if (
+					!SummonEffectsTable.getInstance().getServitorEffectsOwner().contains(getOwner().getObjectId()) ||
+					!SummonEffectsTable.getInstance().getServitorEffectsOwner().get(getOwner().getObjectId()).contains(getOwner().getClassIndex()) ||
+					!SummonEffectsTable.getInstance().getServitorEffects(getOwner()).contains(getReferenceSkill())
+				)
+			{
+				statement = con.prepareStatement(RESTORE_SKILL_SAVE);
+				statement.setInt(1, getOwner().getObjectId());
+				statement.setInt(2, getOwner().getClassIndex());
+				statement.setInt(3, getReferenceSkill());
+				ResultSet rset = statement.executeQuery();
+				
+				while (rset.next())
+				{
+					int effectCount = rset.getInt("effect_count");
+					int effectCurTime = rset.getInt("effect_cur_time");
+					
+					final L2Skill skill = SkillTable.getInstance().getInfo(rset.getInt("skill_id"), rset.getInt("skill_level"));
+					if (skill == null)
+						continue;
+					
+					if (skill.hasEffects())
+					{
+						if (!SummonEffectsTable.getInstance().getServitorEffectsOwner().contains(getOwner().getObjectId())) // Check if charId exists in map
+							SummonEffectsTable.getInstance().getServitorEffectsOwner().put(getOwner().getObjectId(), new TIntObjectHashMap<TIntObjectHashMap<List<SummonEffect>>>());
+						if (!SummonEffectsTable.getInstance().getServitorEffectsOwner().get(getOwner().getObjectId()).contains(getOwner().getClassIndex())) // Check if classIndex exists in charId map
+							SummonEffectsTable.getInstance().getServitorEffectsOwner().get(getOwner().getObjectId()).put(getOwner().getClassIndex(), new TIntObjectHashMap<List<SummonEffect>>());
+						if (!SummonEffectsTable.getInstance().getServitorEffects(getOwner()).contains(getReferenceSkill())) // Check is summonSkillId exists in charId+classIndex map
+							SummonEffectsTable.getInstance().getServitorEffects(getOwner()).put(getReferenceSkill(), new FastList<SummonEffect>());
+						
+						SummonEffectsTable.getInstance().getServitorEffects(getOwner()).get(getReferenceSkill()).add(SummonEffectsTable.getInstance().new SummonEffect(skill, effectCount, effectCurTime));
+					}
+				}
+				
+				rset.close();
+				statement.close();
+			}
+			
+			statement = con.prepareStatement(DELETE_SKILL_SAVE);
+			statement.setInt(1, getOwner().getObjectId());
+			statement.setInt(2, getOwner().getClassIndex());
+			statement.setInt(3, getReferenceSkill());
+			statement.executeUpdate();
+			statement.close();
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.WARNING, "Could not restore " + this + " active effect data: " + e.getMessage(), e);
+		}
+		finally
+		{
+			L2DatabaseFactory.close(con);
+			if (
+					!SummonEffectsTable.getInstance().getServitorEffectsOwner().contains(getOwner().getObjectId()) ||
+					!SummonEffectsTable.getInstance().getServitorEffectsOwner().get(getOwner().getObjectId()).contains(getOwner().getClassIndex()) ||
+					!SummonEffectsTable.getInstance().getServitorEffects(getOwner()).contains(getReferenceSkill())
+				)
+				return;
+
+			for (SummonEffect se : SummonEffectsTable.getInstance().getServitorEffects(getOwner()).get(getReferenceSkill()))
+			{
+				Env env = new Env();
+				env.player = this;
+				env.target = this;
+				env.skill = se.getSkill();
+				L2Effect ef;
+				for (EffectTemplate et : se.getSkill().getEffectTemplates())
+				{
+					ef = et.getEffect(env);
+					if (ef != null)
+					{
+						ef.setCount(se.getEffectCount());
+						ef.setFirstTime(se.getEffectCurTime());
+						ef.scheduleEffect();
+					}
+				}
+			}
+		}
+	}
+	
 	static class SummonLifetime implements Runnable
 	{
 		private L2PcInstance _activeChar;
@@ -300,6 +529,9 @@ public class L2SummonInstance extends L2Summon
 		}
 		
 		super.unSummon(owner);
+		
+		if (!_restoreSummon)
+			CharSummonTable.getInstance().removeServitor(owner);
 	}
 	
 	@Override
@@ -345,4 +577,14 @@ public class L2SummonInstance extends L2Summon
 		// bonus from owner
 		return super.getDefenseElementValue(attribute) + getOwner().getDefenseElementValue(attribute);
 	}
+	
+	public void setTimeRemaining(int time)
+	{
+		_timeRemaining = time;
+	}
+	
+	public int getReferenceSkill()
+	{
+		return _referenceSkill;
+	}
 }

+ 10 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/skills/l2skills/L2SkillSummon.java

@@ -300,4 +300,14 @@ public class L2SkillSummon extends L2Skill
 	{
 		return _itemConsumeTime;
 	}
+	
+	public final int getNpcId()
+	{
+		return _npcId;
+	}
+	
+	public final float getExpPenalty()
+	{
+		return _expPenalty;
+	}
 }

+ 17 - 1
L2J_Server_BETA/java/config/Character.properties

@@ -134,7 +134,7 @@ StoreSkillCooltime = True
 
 # This option is to enable or disable the storage of buffs/debuffs among other effects during
 # a subclass change
-# Deault: False
+# Default: False
 SubclassStoreSkillCooltime = False
 
 # These are alternative rules for shields.
@@ -211,6 +211,22 @@ AltSubClassWithoutQuests = False
 # Default: False
 AltSubclassEverywhere = False
 
+
+# ---------------------------------------------------------------------------
+# Summons configuration
+# ---------------------------------------------------------------------------
+# This option is to enable or disable the storage of buffs/debuffs among other effects on pets/invocations
+# Default: True
+SummonStoreSkillCooltime = True
+
+# Servitor summons on login if player had it summoned before logout
+# Default: True
+RestoreServitorOnReconnect = True
+
+# Pet summons on login if player had it summoned before logout
+# Default: True
+RestorePetOnReconnect = True
+
 # ---------------------------------------------------------------------------
 # Vitality configuration
 # ---------------------------------------------------------------------------