Browse Source

BETA: Sweeper skill rework:
* Removed from target aura corpse sweep conditions.
* Added corpse time check for sweeper, now sweep is possible if the corpse isn't too old.
* Removed sweep conditions from L2PcInstance.
* Added new function to check if player is spoil loot owner or is in the spoiler party.
* Added new function to validate inventory slots and weight load for a list of items, now sweep is possible if max inventory slots and max weight load aren't exceed.
* Added player condition "can sweep".
Others:
* Unhardcoded decay times and DecayTaskManager reworked a bit.
* Changed "corpse too old" check for DRAIN type skills.
* Lucky skill Id is now read from L2Skill.

Zoey76 14 years ago
parent
commit
749cfbfb1c

+ 8 - 0
L2J_Server_BETA/java/com/l2jserver/Config.java

@@ -756,6 +756,10 @@ public final class Config
 	public static TIntFloatHashMap NPC_SKILL_DMG_PENALTY;
 	public static int MIN_NPC_LVL_MAGIC_PENALTY;
 	public static TIntFloatHashMap NPC_SKILL_CHANCE_PENALTY;
+	public static int DECAY_TIME_TASK;
+	public static int NPC_DECAY_TIME;
+	public static int RAID_BOSS_DECAY_TIME;
+	public static int SPOILED_DECAY_TIME;
 	public static boolean GUARD_ATTACK_AGGRO_MOB;
 	public static boolean ALLOW_WYVERN_UPGRADER;
 	public static TIntArrayList LIST_PET_RENT_NPC;
@@ -2077,6 +2081,10 @@ public final class Config
 					NPC_SKILL_DMG_PENALTY = parseConfigLine(NPC.getProperty("SkillDmgPenaltyForLvLDifferences", "0.8, 0.7, 0.65, 0.62"));
 					MIN_NPC_LVL_MAGIC_PENALTY = Integer.parseInt(NPC.getProperty("MinNPCLevelForMagicPenalty", "78"));
 					NPC_SKILL_CHANCE_PENALTY = parseConfigLine(NPC.getProperty("SkillChancePenaltyForLvLDifferences", "2.5, 3.0, 3.25, 3.5"));
+					DECAY_TIME_TASK = Integer.parseInt(NPC.getProperty("DecayTimeTask", "5000"));
+					NPC_DECAY_TIME = Integer.parseInt(NPC.getProperty("NpcDecayTime", "8500"));
+					RAID_BOSS_DECAY_TIME = Integer.parseInt(NPC.getProperty("RaidBossDecayTime", "30000"));
+					SPOILED_DECAY_TIME = Integer.parseInt(NPC.getProperty("SpoiledDecayTime", "18500"));
 					ENABLE_DROP_VITALITY_HERBS = Boolean.parseBoolean(NPC.getProperty("EnableVitalityHerbs", "True"));
 					GUARD_ATTACK_AGGRO_MOB = Boolean.parseBoolean(NPC.getProperty("GuardAttackAggroMob", "False"));
 					ALLOW_WYVERN_UPGRADER = Boolean.parseBoolean(NPC.getProperty("AllowWyvernUpgrader", "False"));

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

@@ -45,7 +45,6 @@ import com.l2jserver.gameserver.model.actor.instance.L2SiegeFlagInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2SummonInstance;
 import com.l2jserver.gameserver.model.entity.TvTEvent;
 import com.l2jserver.gameserver.network.SystemMessageId;
-//TODO: import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.skills.BaseStats;
 import com.l2jserver.gameserver.skills.Env;
@@ -54,7 +53,6 @@ import com.l2jserver.gameserver.skills.Stats;
 import com.l2jserver.gameserver.skills.conditions.Condition;
 import com.l2jserver.gameserver.skills.funcs.Func;
 import com.l2jserver.gameserver.skills.funcs.FuncTemplate;
-import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
 import com.l2jserver.gameserver.templates.StatsSet;
 import com.l2jserver.gameserver.templates.effects.EffectTemplate;
 import com.l2jserver.gameserver.templates.item.L2Armor;
@@ -2142,10 +2140,8 @@ public abstract class L2Skill implements IChanceSkillTrigger
 					}
 					case DRAIN:
 					{
-						if (DecayTaskManager.getInstance().getTasks().containsKey(target)
-								&& (System.currentTimeMillis() - DecayTaskManager.getInstance().getTasks().get(target)) > DecayTaskManager.ATTACKABLE_DECAY_TIME / 2)
+						if (((L2Attackable) target).checkCorpseTime(activeChar.getActingPlayer(), (Config.NPC_DECAY_TIME / 2), true))
 						{
-							activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CORPSE_TOO_OLD_SKILL_NOT_USED));
 							return _emptyTargetList;
 						}
 					}
@@ -2198,28 +2194,10 @@ public abstract class L2Skill implements IChanceSkillTrigger
 			}
 			case TARGET_AURA_CORPSE_MOB:
 			{
-				final Collection<L2Character> characters = activeChar.getKnownList().getKnownCharactersInRadius(getSkillRadius());
-				L2Attackable corpseMob;
-				int spoilerId;
-				for (L2Character character : characters)
+				for (L2Character character : activeChar.getKnownList().getKnownCharactersInRadius(getSkillRadius()))
 				{
 					if ((character instanceof L2Attackable) && character.isDead())
 					{
-						if (getSkillType() == L2SkillType.SWEEP)
-						{
-							corpseMob = (L2Attackable) character;
-							//If target is not spoiled, ignore it.
-							if (!corpseMob.isSpoil())
-							{
-								continue;
-							}
-							spoilerId = corpseMob.getIsSpoiledBy();
-							//If target is not spoiled by the caster or a party member, ignore it.
-							if ((activeChar.getObjectId() != spoilerId) && (activeChar.getActingPlayer() != null) && !activeChar.getActingPlayer().isInLooterParty(spoilerId))
-							{
-								continue;
-							}
-						}
 						targetList.add(character);
 					}
 				}

+ 54 - 2
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/L2Attackable.java

@@ -60,8 +60,10 @@ import com.l2jserver.gameserver.network.clientpackets.Say2;
 import com.l2jserver.gameserver.network.serverpackets.CreatureSay;
 import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 import com.l2jserver.gameserver.skills.Stats;
+import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
 import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
 import com.l2jserver.gameserver.templates.item.L2EtcItemType;
+import com.l2jserver.gameserver.templates.item.L2Item;
 import com.l2jserver.gameserver.util.Util;
 import com.l2jserver.util.Rnd;
 
@@ -1879,6 +1881,22 @@ public class L2Attackable extends L2Npc
 		return _sweepItems != null;
 	}
 	
+	/**
+	 * @return a copy of dummy items for the spoil loot.
+	 */
+	public FastList<L2Item> getSpoilLootItems()
+	{
+		final FastList<L2Item> lootItems = new FastList<L2Item>();
+		if (isSweepActive())
+		{
+			for (RewardItem item : _sweepItems)
+			{
+				lootItems.add(ItemTable.getInstance().createDummyItem(item.getItemId()).getItem());
+			}
+		}
+		return lootItems;
+	}
+	
 	/**
 	 * Return table containing all L2ItemInstance that can be spoiled.
 	 */
@@ -1899,11 +1917,46 @@ public class L2Attackable extends L2Npc
 		return harvest;
 	}
 	
+	/**
+	 * @param target the spoiled monster.
+	 * @param sendMessage if {@code true} will send a message of corpse too old.
+	 * @return {@code true} if the corpse isn't too old.
+	 */
+	public boolean checkCorpseTime(L2PcInstance attacker, int time, boolean sendMessage)
+	{
+		if (DecayTaskManager.getInstance().getTasks().containsKey(this) && ((System.currentTimeMillis() - DecayTaskManager.getInstance().getTasks().get(this)) > time))
+		{
+			if (sendMessage && (attacker != null))
+			{
+				attacker.sendPacket(SystemMessageId.CORPSE_TOO_OLD_SKILL_NOT_USED);
+			}
+			return false;
+		}
+		return true;
+	}
+	
+	/**
+	 * @param sweeper the player to validate.
+	 * @param sendMessage sendMessage if {@code true} will send a message of sweep not allowed.
+	 * @return {@code true} if is the spoiler or is in the spoiler party.
+	 */
+	public boolean checkSpoilOwner(L2PcInstance sweeper, boolean sendMessage)
+	{
+		if ((sweeper.getObjectId() != getIsSpoiledBy()) && !sweeper.isInLooterParty(getIsSpoiledBy()))
+		{
+			if (sendMessage)
+			{
+				sweeper.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SWEEP_NOT_ALLOWED));
+			}
+			return false;
+		}
+		return true;
+	}
+	
 	/**
 	 * Set the over-hit flag on the L2Attackable.
 	 *
 	 * @param status The status of the over-hit flag
-	 *
 	 */
 	public void overhitEnabled(boolean status)
 	{
@@ -1915,7 +1968,6 @@ public class L2Attackable extends L2Npc
 	 *
 	 * @param attacker The L2Character who hit on the L2Attackable using the over-hit enabled skill
 	 * @param damage The ammount of damage done by the over-hit enabled skill on the L2Attackable
-	 *
 	 */
 	public void setOverhitValues(L2Character attacker, double damage)
 	{

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

@@ -5806,7 +5806,7 @@ public final class L2PcInstance extends L2Playable
 	 */
 	public boolean isLucky()
 	{
-		return ((getLevel() <= 9) && (getKnownSkill(194) != null));
+		return ((getLevel() <= 9) && (getKnownSkill(L2Skill.SKILL_LUCKY) != null));
 	}
 	
 	/**
@@ -9035,35 +9035,6 @@ public final class L2PcInstance extends L2Playable
 			}
 		}
 		
-		// Check if the skill is Sweep type and if conditions not apply
-		if (sklType == L2SkillType.SWEEP && target instanceof L2Attackable)
-		{
-			int spoilerId = ((L2Attackable) target).getIsSpoiledBy();
-			
-			if (((L2Attackable) target).isDead())
-			{
-				if (!((L2Attackable) target).isSpoil())
-				{
-					// Send a System Message to the L2PcInstance
-					sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SWEEPER_FAILED_TARGET_NOT_SPOILED));
-					
-					// Send a Server->Client packet ActionFailed to the L2PcInstance
-					sendPacket(ActionFailed.STATIC_PACKET);
-					return false;
-				}
-				
-				if (getObjectId() != spoilerId && !isInLooterParty(spoilerId))
-				{
-					// Send a System Message to the L2PcInstance
-					sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SWEEP_NOT_ALLOWED));
-					
-					// Send a Server->Client packet ActionFailed to the L2PcInstance
-					sendPacket(ActionFailed.STATIC_PACKET);
-					return false;
-				}
-			}
-		}
-		
 		// Check if the skill is Drain Soul (Soul Crystals) and if the target is a MOB
 		if (sklType == L2SkillType.DRAIN_SOUL)
 		{

+ 59 - 24
L2J_Server_BETA/java/com/l2jserver/gameserver/model/itemcontainer/PcInventory.java

@@ -17,7 +17,6 @@ package com.l2jserver.gameserver.model.itemcontainer;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
-import java.util.List;
 import java.util.logging.Level;
 
 import javolution.util.FastList;
@@ -30,6 +29,7 @@ import com.l2jserver.gameserver.model.L2ItemInstance.ItemLocation;
 import com.l2jserver.gameserver.model.TradeList;
 import com.l2jserver.gameserver.model.TradeList.TradeItem;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate;
 import com.l2jserver.gameserver.network.serverpackets.ItemList;
 import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
@@ -711,38 +711,70 @@ public class PcInventory extends Inventory
 		return paperdoll;
 	}
 	
-	
-	public boolean validateCapacity(L2ItemInstance item)
+	/**
+	 * @param itemList the items that needs to be validated.
+	 * @param sendMessage if {@code true} will send a message of inventory full.
+	 * @param sendSkillMessage if {@code true} will send a message of skill not available.
+	 * @return {@code true} if the inventory isn't full after taking new items and items weight add to current load doesn't exceed max weight load.
+	 */
+	public boolean checkInventorySlotsAndWeight(FastList<L2Item> itemList, boolean sendMessage, boolean sendSkillMessage)
 	{
-		int slots = 0;
-		
-		if (!(item.isStackable() && getItemByItemId(item.getItemId()) != null) && item.getItemType() != L2EtcItemType.HERB)
-			slots++;
+		int lootWeight = 0;
+		int requiredSlots = 0;
+		if (itemList != null)
+		{
+			for (L2Item item : itemList)
+			{
+				//If the item is not stackable or is stackable and not present in inventory, will need a slot.
+				if (!item.isStackable() || (getInventoryItemCount(item.getItemId(), -1) <= 0))
+				{
+					requiredSlots++;
+				}
+				lootWeight += item.getWeight();
+			}
+		}
 		
-		return validateCapacity(slots, item.isQuestItem());
+		boolean inventoryStatusOK = validateCapacity(requiredSlots) && validateWeight(lootWeight);
+		if (!inventoryStatusOK && sendMessage)
+		{
+			_owner.sendPacket(SystemMessageId.SLOTS_FULL);
+			if (sendSkillMessage)
+			{
+				_owner.sendPacket(SystemMessageId.WEIGHT_EXCEEDED_SKILL_UNAVAILABLE);
+			}
+		}
+		return inventoryStatusOK;
 	}
 	
-	@Deprecated
-	public boolean validateCapacity(List<L2ItemInstance> items)
+	/**
+	 * If the item is not stackable or is stackable and not present in inventory, will need a slot.
+	 * @param item the item to validate.
+	 * @return {@code true} if there is enough room to add the item inventory.
+	 */
+	public boolean validateCapacity(L2ItemInstance item)
 	{
 		int slots = 0;
-		
-		for (L2ItemInstance item : items)
-			if (!(item.isStackable() && getItemByItemId(item.getItemId()) != null))
-				slots++;
-		
-		return validateCapacity(slots);
+		if (!item.isStackable() || (getInventoryItemCount(item.getItemId(), -1) <= 0) || (item.getItemType() != L2EtcItemType.HERB))
+		{
+			slots++;
+		}
+		return validateCapacity(slots, item.isQuestItem());
 	}
 	
-	public boolean validateCapacityByItemId(int ItemId)
+	/**
+	 * If the item is not stackable or is stackable and not present in inventory, will need a slot.
+	 * @param itemId the item Id for the item to validate.
+	 * @return {@code true} if there is enough room to add the item inventory.
+	 */
+	public boolean validateCapacityByItemId(int itemId)
 	{
 		int slots = 0;
-		L2Item item = ItemTable.getInstance().getTemplate(ItemId);
-		L2ItemInstance invItem = getItemByItemId(ItemId);
-		if (!(invItem != null && invItem.isStackable()))
+		final L2ItemInstance invItem = getItemByItemId(itemId);
+		if ((invItem == null) || !invItem.isStackable())
+		{
 			slots++;
-		
-		return validateCapacity(slots, item.isQuestItem());
+		}
+		return validateCapacity(slots, ItemTable.getInstance().getTemplate(itemId).isQuestItem());
 	}
 	
 	@Override
@@ -762,8 +794,11 @@ public class PcInventory extends Inventory
 	@Override
 	public boolean validateWeight(int weight)
 	{
-		if (_owner.isGM() && _owner.getAccessLevel().allowTransaction())
-			return true; // disable weight check for GM
+		// Disable weight check for GMs.
+		if (_owner.isGM() && _owner.getDietMode() && _owner.getAccessLevel().allowTransaction())
+		{
+			return true;
+		}
 		return (_totalWeight + weight <= _owner.getMaxLoad());
 	}
 	

+ 5 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/skills/DocumentBase.java

@@ -51,6 +51,7 @@ import com.l2jserver.gameserver.skills.conditions.ConditionMinDistance;
 import com.l2jserver.gameserver.skills.conditions.ConditionPlayerActiveEffectId;
 import com.l2jserver.gameserver.skills.conditions.ConditionPlayerActiveSkillId;
 import com.l2jserver.gameserver.skills.conditions.ConditionPlayerAgathionId;
+import com.l2jserver.gameserver.skills.conditions.ConditionPlayerCanSweep;
 import com.l2jserver.gameserver.skills.conditions.ConditionPlayerCharges;
 import com.l2jserver.gameserver.skills.conditions.ConditionPlayerClassIdRestriction;
 import com.l2jserver.gameserver.skills.conditions.ConditionPlayerCloakStatus;
@@ -739,6 +740,10 @@ abstract class DocumentBase
 				}
 				cond = joinAnd(cond, new ConditionPlayerRangeFromNpc(npcId, radius));
 			}
+			else if ("canSweep".equalsIgnoreCase(a.getNodeName()))
+			{
+				cond = joinAnd(cond, new ConditionPlayerCanSweep(Boolean.valueOf(a.getNodeValue())));
+			}
 		}
 		
 		if (forces[0] + forces[1] > 0)

+ 90 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/skills/conditions/ConditionPlayerCanSweep.java

@@ -0,0 +1,90 @@
+/*
+ * 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.skills.conditions;
+
+import com.l2jserver.gameserver.model.L2Object;
+import com.l2jserver.gameserver.model.L2Skill;
+import com.l2jserver.gameserver.model.actor.L2Attackable;
+import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jserver.gameserver.network.SystemMessageId;
+import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
+import com.l2jserver.gameserver.skills.Env;
+
+/**
+ * Checks Sweeper conditions:
+ * <ul>
+ * 	<li>Minimum checks, player not null, skill not null.</li>
+ * 	<li>Checks if the target isn't null, is dead and spoiled.</li>
+ * 	<li>Checks if the sweeper player is the target spoiler, or is in the spoiler party.</li>
+ * 	<li>Checks if the corpse is too old.</li>
+ * 	<li>Checks inventory limit and weight max load won't be exceed after sweep.</li>
+ * </ul>
+ * If two or more conditions aren't meet at the same time, one message per condition will be shown.
+ * @author Zoey76
+ */
+public class ConditionPlayerCanSweep extends Condition
+{
+	private final boolean _val;
+	private static final int maxSweepTime = 15000;
+	public ConditionPlayerCanSweep(boolean val)
+	{
+		_val = val;
+	}
+	
+	@Override
+	public boolean testImpl(Env env)
+	{
+		boolean canSweep = (env.player != null) && (env.player instanceof L2PcInstance);
+		if (canSweep)
+		{
+			final L2PcInstance sweeper = env.player.getActingPlayer();
+			final L2Skill sweep = env.skill;
+			canSweep &= (sweep != null);
+			if (canSweep)
+			{
+				final L2Object[] targets = sweep.getTargetList(sweeper);
+				canSweep &= (targets != null);
+				if (canSweep)
+				{
+					L2Attackable target;
+					for (L2Object objTarget : targets)
+					{
+						canSweep &= (objTarget != null) && (objTarget instanceof L2Attackable);
+						if (canSweep)
+						{
+							target = (L2Attackable) objTarget;
+							canSweep &= target.isDead();
+							if (canSweep)
+							{
+								canSweep &= target.isSpoil();
+								if (canSweep)
+								{
+									canSweep &= target.checkSpoilOwner(sweeper, true);
+									canSweep &= target.checkCorpseTime(sweeper, maxSweepTime, true);
+									canSweep &= sweeper.getInventory().checkInventorySlotsAndWeight(target.getSpoilLootItems(), true, true);
+								}
+								else
+								{
+									sweeper.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SWEEPER_FAILED_TARGET_NOT_SPOILED));
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		return (_val == canSweep);
+	}
+}

+ 18 - 19
L2J_Server_BETA/java/com/l2jserver/gameserver/taskmanager/DecayTaskManager.java

@@ -23,6 +23,7 @@ import java.util.logging.Logger;
 
 import javolution.util.FastMap;
 
+import com.l2jserver.Config;
 import com.l2jserver.gameserver.ThreadPoolManager;
 import com.l2jserver.gameserver.model.actor.L2Attackable;
 import com.l2jserver.gameserver.model.actor.L2Character;
@@ -34,14 +35,11 @@ public class DecayTaskManager
 {
 	protected static final Logger _log = Logger.getLogger(DecayTaskManager.class.getName());
 	
-	protected Map<L2Character, Long> _decayTasks = new FastMap<L2Character, Long>().shared();
-	
-	public static final int RAID_BOSS_DECAY_TIME = 30000;
-	public static final int ATTACKABLE_DECAY_TIME = 8500;
-	
+	protected final Map<L2Character, Long> _decayTasks = new FastMap<L2Character, Long>().shared();
+
 	private DecayTaskManager()
 	{
-		ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new DecayScheduler(), 10000, 5000);
+		ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new DecayScheduler(), 10000, Config.DECAY_TIME_TASK);
 	}
 	
 	public static DecayTaskManager getInstance()
@@ -79,25 +77,27 @@ public class DecayTaskManager
 		
 		public void run()
 		{
-			long current = System.currentTimeMillis();
-			int delay;
+			final long current = System.currentTimeMillis();
 			try
 			{
-				Iterator<Entry<L2Character, Long>> it = _decayTasks.entrySet().iterator();
+				final Iterator<Entry<L2Character, Long>> it = _decayTasks.entrySet().iterator();
+				Entry<L2Character, Long> e;
+				L2Character actor;
+				Long next;
+				int delay;
 				while (it.hasNext())
 				{
-					Entry<L2Character, Long> e = it.next();
-					L2Character actor = e.getKey();
-					Long next = e.getValue();
+					e = it.next();
+					actor = e.getKey();
+					next = e.getValue();
 					if (next == null)
 						continue;
 					if (actor.isRaid() && !actor.isRaidMinion())
-						delay = RAID_BOSS_DECAY_TIME;
-					else if (actor instanceof L2Attackable &&
-							(((L2Attackable)actor).isSpoil() || ((L2Attackable)actor).isSeeded()))
-						delay = ATTACKABLE_DECAY_TIME * 2;
+						delay = Config.RAID_BOSS_DECAY_TIME;
+					else if ((actor instanceof L2Attackable) && (((L2Attackable) actor).isSpoil() || ((L2Attackable) actor).isSeeded()))
+						delay = Config.SPOILED_DECAY_TIME;
 					else
-						delay = ATTACKABLE_DECAY_TIME;
+						delay = Config.NPC_DECAY_TIME;
 					if ((current - next) > delay)
 					{
 						actor.onDecay();
@@ -107,8 +107,7 @@ public class DecayTaskManager
 			}
 			catch (Exception e)
 			{
-				// TODO: Find out the reason for exception. Unless caught here,
-				// mob decay would stop.
+				// TODO: Find out the reason for exception. Unless caught here, mob decay would stop.
 				_log.log(Level.WARNING, "Error in DecayScheduler: " + e.getMessage(), e);
 			}
 		}

+ 12 - 0
L2J_Server_BETA/java/config/NPC.properties

@@ -89,6 +89,18 @@ MinNPCLevelForMagicPenalty = 78
 # Default: unknown
 SkillChancePenaltyForLvLDifferences = 2.5, 3.0, 3.25, 3.5
 
+# ---------------------------------------------------------------------------
+# Monsters
+# ---------------------------------------------------------------------------
+# Decay Time Task (don't set it too low!) (in milliseconds):
+# Default: 5000
+DecayTimeTask = 5000
+# Default: 8500
+NpcDecayTime = 8500
+# Default: 30000
+RaidBossDecayTime = 30000
+# Default: 18500
+SpoiledDecayTime = 18500
 
 # ---------------------------------------------------------------------------
 # Guards