diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/config/General.ini b/L2J_Mobius_1.0_Ertheia/dist/game/config/General.ini index a284270f24..4baf8d7528 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/config/General.ini +++ b/L2J_Mobius_1.0_Ertheia/dist/game/config/General.ini @@ -251,19 +251,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java index d96dcec35d..7ed87cb4f8 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java @@ -1859,10 +1859,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 2f50746c8e..a81769105c 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Npc.java index 256f1184cd..75c99b58bf 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_2.5_Underground/dist/game/config/General.ini b/L2J_Mobius_2.5_Underground/dist/game/config/General.ini index aadbfcd534..e74a1be899 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/config/General.ini +++ b/L2J_Mobius_2.5_Underground/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java index 8a2361ba44..f0943edbd6 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java @@ -1876,10 +1876,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 2f50746c8e..a81769105c 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Npc.java index 885765f492..5997f2f6ca 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_3.0_Helios/dist/game/config/General.ini b/L2J_Mobius_3.0_Helios/dist/game/config/General.ini index aadbfcd534..e74a1be899 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/config/General.ini +++ b/L2J_Mobius_3.0_Helios/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java index 8d6b11586e..28acf144fe 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java @@ -1884,10 +1884,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 2f50746c8e..a81769105c 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Npc.java index 885765f492..5997f2f6ca 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/General.ini b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/General.ini index aadbfcd534..e74a1be899 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/General.ini +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java index 1b4b306fea..131c465532 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java @@ -1864,10 +1864,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 2f50746c8e..a81769105c 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Npc.java index 885765f492..5997f2f6ca 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_5.0_Salvation/dist/game/config/General.ini b/L2J_Mobius_5.0_Salvation/dist/game/config/General.ini index aadbfcd534..e74a1be899 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/config/General.ini +++ b/L2J_Mobius_5.0_Salvation/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java index a934dc7d88..d6908bb807 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java @@ -1940,10 +1940,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 2f50746c8e..a81769105c 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Npc.java index 885765f492..5997f2f6ca 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/config/General.ini b/L2J_Mobius_5.5_EtinasFate/dist/game/config/General.ini index aadbfcd534..e74a1be899 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/config/General.ini +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java index a934dc7d88..d6908bb807 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java @@ -1940,10 +1940,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 2f50746c8e..a81769105c 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Npc.java index 885765f492..5997f2f6ca 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/config/General.ini b/L2J_Mobius_6.0_Fafurion/dist/game/config/General.ini index aadbfcd534..e74a1be899 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/config/General.ini +++ b/L2J_Mobius_6.0_Fafurion/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java index 38d85af5f8..4dedaf02a7 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java @@ -1970,10 +1970,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 2f50746c8e..a81769105c 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Npc.java index 885765f492..5997f2f6ca 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_C6_Interlude/dist/game/config/main/Options.ini b/L2J_Mobius_C6_Interlude/dist/game/config/main/Options.ini index 4478cb8ada..deff03ce96 100644 --- a/L2J_Mobius_C6_Interlude/dist/game/config/main/Options.ini +++ b/L2J_Mobius_C6_Interlude/dist/game/config/main/Options.ini @@ -107,15 +107,14 @@ ForceCompletePlayerStatusUpdate = True # The maximum deviation from the point of Spawn mobs MaxDriftRange = 200 -# Minimum and maximum delay animation -# The minimum can not be greater than the maximum. -# "0" by default. -MinNPCAnimation = 10 -MaxNPCAnimation = 20 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +MinNpcAnimation = 5 +MaxNpcAnimation = 60 MinMonsterAnimation = 5 -MaxMonsterAnimation = 20 +MaxMonsterAnimation = 60 -# Show the lvl and type of agro mobs? +# Show the lvl and type of aggro mobs? ShowNpcLevel = False # Record the location of the characters in the file before the off / restarting the server? diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java index af7731e0a1..b3e1b6039e 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java @@ -1287,10 +1287,10 @@ public final class Config MAX_DRIFT_RANGE = Integer.parseInt(optionsSettings.getProperty("MaxDriftRange", "300")); - MIN_NPC_ANIMATION = Integer.parseInt(optionsSettings.getProperty("MinNPCAnimation", "10")); - MAX_NPC_ANIMATION = Integer.parseInt(optionsSettings.getProperty("MaxNPCAnimation", "20")); + MIN_NPC_ANIMATION = Integer.parseInt(optionsSettings.getProperty("MinNpcAnimation", "5")); + MAX_NPC_ANIMATION = Integer.parseInt(optionsSettings.getProperty("MaxNpcAnimation", "60")); MIN_MONSTER_ANIMATION = Integer.parseInt(optionsSettings.getProperty("MinMonsterAnimation", "5")); - MAX_MONSTER_ANIMATION = Integer.parseInt(optionsSettings.getProperty("MaxMonsterAnimation", "20")); + MAX_MONSTER_ANIMATION = Integer.parseInt(optionsSettings.getProperty("MaxMonsterAnimation", "60")); SHOW_NPC_LVL = Boolean.valueOf(optionsSettings.getProperty("ShowNpcLevel", "false")); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 12b8b29aa2..4fe4903e73 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -30,14 +30,12 @@ import org.l2jmobius.gameserver.ai.PlayerAI.IntentionCommand; import org.l2jmobius.gameserver.model.Inventory; import org.l2jmobius.gameserver.model.Skill; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Playable; import org.l2jmobius.gameserver.model.actor.instance.BoatInstance; import org.l2jmobius.gameserver.model.actor.instance.DoorInstance; import org.l2jmobius.gameserver.model.actor.instance.ItemInstance; import org.l2jmobius.gameserver.model.actor.instance.ItemInstance.ItemLocation; -import org.l2jmobius.gameserver.model.actor.instance.NpcInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.model.actor.position.Location; import org.l2jmobius.gameserver.model.zone.ZoneId; @@ -147,13 +145,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor instanceof Attackable) - { - ((NpcInstance) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } @@ -1215,13 +1206,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor instanceof Attackable) - { - ((NpcInstance) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldRegion.java index 779136d6f4..22c64fbe14 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -37,6 +37,7 @@ import org.l2jmobius.gameserver.model.spawn.Spawn; import org.l2jmobius.gameserver.model.zone.ZoneManager; import org.l2jmobius.gameserver.model.zone.ZoneType; import org.l2jmobius.gameserver.model.zone.type.PeaceZone; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { @@ -222,6 +223,12 @@ public final class WorldRegion ((SiegeGuardAI) mob.getAI()).stopAITask(); } } + + RandomAnimationManager.getInstance().remove(mob); + } + else if (o instanceof NpcInstance) + { + RandomAnimationManager.getInstance().remove((NpcInstance) o); } } } @@ -233,12 +240,11 @@ public final class WorldRegion { // Start HP/MP/CP Regeneration task ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((NpcInstance) o); } else if (o instanceof NpcInstance) { - // Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it - // Monsterinstance/Attackable socials are handled by AI (TODO: check the instances) - ((NpcInstance) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((NpcInstance) o); } } } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/NpcInstance.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/NpcInstance.java index 21e3bab978..c9cb6d2b41 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/NpcInstance.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/instance/NpcInstance.java @@ -16,14 +16,11 @@ */ package org.l2jmobius.gameserver.model.actor.instance; -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - import java.text.DateFormat; import java.util.ArrayList; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.ai.CtrlIntention; import org.l2jmobius.gameserver.cache.HtmCache; @@ -131,116 +128,12 @@ public class NpcInstance extends Creature private int _isSpoiledBy = 0; private long _lastSocialBroadcast = 0; private static final int MINIMUM_SOCIAL_INTERVAL = 6000; - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentCollisionHeight; // used for npc grow effect skills private int _currentCollisionRadius; // used for npc grow effect skills private int _scriptValue = 0; - public class RandomAnimationTask implements Runnable - { - private final NpcInstance _npc; - private boolean _stopTask; - - public RandomAnimationTask(NpcInstance npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!(_npc.isDead() || _npc.isStunned() || _npc.isSleeping() || _npc.isParalyzed())) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } - } - - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..e14993b507 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,91 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Attackable; +import org.l2jmobius.gameserver.model.actor.instance.NpcInstance; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final NpcInstance npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if ((npc instanceof Attackable) && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.isStunned() && !npc.isSleeping() && !npc.isParalyzed()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(NpcInstance npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(NpcInstance npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/General.ini b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/General.ini index 2ca87e22c2..92274be142 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/General.ini +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/General.ini @@ -244,19 +244,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java index 159d5f79bd..bbd2b7be98 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java @@ -2065,10 +2065,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 78434bf96b..0ef99d6297 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -203,13 +203,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldRegion.java index 561a4bf5e7..c50c3f5c39 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Npc.java index 4333735c63..9a0396229d 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -63,7 +63,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TrainerInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.Fort; @@ -139,7 +138,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -242,37 +240,6 @@ public class Npc extends Creature return getTemplate().getAISkills(AISkillScope.SHORT_RANGE); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 6e3378e5bd..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!(_npc.isDead() || _npc.isStunned() || _npc.isSleeping() || _npc.isParalyzed())) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..a6d73fb472 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.isStunned() && !npc.isSleeping() && !npc.isParalyzed()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/General.ini b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/General.ini index 595ab5efae..7a2de4c9f5 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/General.ini +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java index 124ef53fed..3c026be4c2 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java @@ -1785,10 +1785,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 359cd70b74..e94ecaae84 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Npc.java index 494438e5fb..fae0c8e512 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/General.ini b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/General.ini index 595ab5efae..7a2de4c9f5 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/General.ini +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java index ac931dbe54..bcfb106dde 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java @@ -1789,10 +1789,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 359cd70b74..e94ecaae84 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Npc.java index 494438e5fb..fae0c8e512 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/General.ini b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/General.ini index 595ab5efae..7a2de4c9f5 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/General.ini +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java index ac931dbe54..bcfb106dde 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java @@ -1789,10 +1789,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 359cd70b74..e94ecaae84 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Npc.java index 494438e5fb..fae0c8e512 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/General.ini b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/General.ini index 595ab5efae..7a2de4c9f5 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/General.ini +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java index ac931dbe54..bcfb106dde 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java @@ -1789,10 +1789,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 359cd70b74..e94ecaae84 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Npc.java index 494438e5fb..fae0c8e512 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/General.ini b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/General.ini index 595ab5efae..7a2de4c9f5 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/General.ini +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/General.ini @@ -255,19 +255,19 @@ LazyCache = False # Default: True CacheCharNames = True -# Minimum and maximum variables in seconds for npc animation delay. -# You must keep MinNPCAnimation < = MaxNPCAnimation. -# Default: 10 -MinNPCAnimation = 10 +# Minimum and maximum variables in seconds for NPC animation delay. +# You must keep MinNpcAnimation < = MaxNpcAnimation. +# Default: 5 +MinNpcAnimation = 5 -# Default: 20 -MaxNPCAnimation = 20 +# Default: 60 +MaxNpcAnimation = 60 # Default: 5 MinMonsterAnimation = 5 -# Default: 20 -MaxMonsterAnimation = 20 +# Default: 60 +MaxMonsterAnimation = 60 # Grid options: Grids can turn themselves on and off. This also affects the loading and processing of all AI tasks and (in the future) geodata within this grid. # Turn on for a grid with a person in it is immediate, but it then turns on the 8 neighboring grids based on the specified number of seconds. diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java index ac931dbe54..bcfb106dde 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java @@ -1789,10 +1789,10 @@ public final class Config FORCE_INVENTORY_UPDATE = General.getBoolean("ForceInventoryUpdate", false); LAZY_CACHE = General.getBoolean("LazyCache", true); CACHE_CHAR_NAMES = General.getBoolean("CacheCharNames", true); - MIN_NPC_ANIMATION = General.getInt("MinNPCAnimation", 10); - MAX_NPC_ANIMATION = General.getInt("MaxNPCAnimation", 20); + MIN_NPC_ANIMATION = General.getInt("MinNpcAnimation", 5); + MAX_NPC_ANIMATION = General.getInt("MaxNpcAnimation", 60); MIN_MONSTER_ANIMATION = General.getInt("MinMonsterAnimation", 5); - MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 20); + MAX_MONSTER_ANIMATION = General.getInt("MaxMonsterAnimation", 60); GRIDS_ALWAYS_ON = General.getBoolean("GridsAlwaysOn", false); GRID_NEIGHBOR_TURNON_TIME = General.getInt("GridNeighborTurnOnTime", 1); GRID_NEIGHBOR_TURNOFF_TIME = General.getInt("GridNeighborTurnOffTime", 90); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 359cd70b74..e94ecaae84 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -188,13 +188,6 @@ public class CreatureAI extends AbstractAI // Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop (broadcast) clientStopAutoAttack(); - // Also enable random animations for this Creature if allowed - // This is only for mobs - town npcs are handled in their constructor - if (_actor.isAttackable()) - { - ((Npc) _actor).startRandomAnimationTask(); - } - // Launch the Think Event onEvtThink(); } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldRegion.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldRegion.java index 9005589fff..600ac215c4 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldRegion.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldRegion.java @@ -20,18 +20,15 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.function.Predicate; -import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.gameserver.model.actor.Attackable; import org.l2jmobius.gameserver.model.actor.Npc; -import org.l2jmobius.gameserver.model.actor.Vehicle; +import org.l2jmobius.gameserver.taskmanager.RandomAnimationManager; public final class WorldRegion { - private static final Logger LOGGER = Logger.getLogger(WorldRegion.class.getName()); - /** Map containing visible objects in this world region. */ private volatile Map _visibleObjects = new ConcurrentHashMap<>(); /** Map containing nearby regions forming this world region's effective area. */ @@ -81,14 +78,12 @@ public final class WorldRegion return; } - int c = 0; if (!isOn) { for (WorldObject o : _visibleObjects.values()) { if (o.isAttackable()) { - c++; final Attackable mob = (Attackable) o; // Set target to null and cancel attack or cast. @@ -109,13 +104,14 @@ public final class WorldRegion mob.getAI().setIntention(org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE); mob.getAI().stopAITask(); } + + RandomAnimationManager.getInstance().remove(mob); } - else if (o instanceof Vehicle) + else if (o instanceof Npc) { - c++; + RandomAnimationManager.getInstance().remove((Npc) o); } } - LOGGER.finer(c + " mobs were turned off"); } else { @@ -123,16 +119,15 @@ public final class WorldRegion { if (o.isAttackable()) { - c++; // Start HP/MP/CP regeneration task. ((Attackable) o).getStatus().startHpMpRegeneration(); + RandomAnimationManager.getInstance().add((Npc) o); } else if (o instanceof Npc) { - ((Npc) o).startRandomAnimationTask(); + RandomAnimationManager.getInstance().add((Npc) o); } } - LOGGER.finer(c + " mobs were turned on"); } } @@ -161,8 +156,6 @@ public final class WorldRegion // Turn the AI on or off to match the region's activation. switchAI(value); - - LOGGER.finer((value ? "Starting" : "Stopping") + " Grid " + this); } /** diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Npc.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Npc.java index 494438e5fb..fae0c8e512 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Npc.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Npc.java @@ -59,7 +59,6 @@ import org.l2jmobius.gameserver.model.actor.instance.TeleporterInstance; import org.l2jmobius.gameserver.model.actor.instance.WarehouseInstance; import org.l2jmobius.gameserver.model.actor.stat.NpcStat; import org.l2jmobius.gameserver.model.actor.status.NpcStatus; -import org.l2jmobius.gameserver.model.actor.tasks.npc.RandomAnimationTask; import org.l2jmobius.gameserver.model.actor.templates.NpcTemplate; import org.l2jmobius.gameserver.model.entity.Castle; import org.l2jmobius.gameserver.model.entity.ClanHall; @@ -132,7 +131,6 @@ public class Npc extends Creature private final boolean _isQuestMonster = getTemplate().isQuestMonster(); private final boolean _isFakePlayer = getTemplate().isFakePlayer(); - protected RandomAnimationTask _rAniTask; private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions @@ -189,37 +187,6 @@ public class Npc extends Creature initStatusUpdateCache(); } - public void startRandomAnimationTask() - { - if (!hasRandomAnimation()) - { - return; - } - - if (_rAniTask == null) - { - synchronized (this) - { - if (_rAniTask == null) - { - _rAniTask = new RandomAnimationTask(this); - } - } - } - - _rAniTask.startRandomAnimationTimer(); - } - - public void stopRandomAnimationTask() - { - final RandomAnimationTask rAniTask = _rAniTask; - if (rAniTask != null) - { - rAniTask.stopRandomAnimationTimer(); - _rAniTask = null; - } - } - /** * Send a packet SocialAction to all PlayerInstance in the _KnownPlayers of the NpcInstance and create a new RandomAnimation Task. * @param animationId diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java deleted file mode 100644 index 3e77c5058d..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/tasks/npc/RandomAnimationTask.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package org.l2jmobius.gameserver.model.actor.tasks.npc; - -import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.commons.concurrent.ThreadPool; -import org.l2jmobius.commons.util.Rnd; -import org.l2jmobius.gameserver.model.actor.Npc; - -/** - * @author Nik - */ -public class RandomAnimationTask implements Runnable -{ - private static final Logger LOGGER = Logger.getLogger(RandomAnimationTask.class.getName()); - private final Npc _npc; - private boolean _stopTask; - - public RandomAnimationTask(Npc npc) - { - _npc = npc; - } - - @Override - public void run() - { - if (_stopTask) - { - return; - } - - try - { - if (!_npc.isInActiveRegion()) - { - return; - } - - // Cancel further animation timers until intention is changed to ACTIVE again. - if (_npc.isAttackable() && (_npc.getAI().getIntention() != AI_INTENTION_ACTIVE)) - { - return; - } - - if (!_npc.isDead() && !_npc.hasBlockActions()) - { - _npc.onRandomAnimation(Rnd.get(2, 3)); - } - - startRandomAnimationTimer(); - } - catch (Exception e) - { - LOGGER.log(Level.SEVERE, "Execution of RandomAnimationTask has failed.", e); - } - } - - /** - * Create a RandomAnimation Task that will be launched after the calculated delay. - */ - public void startRandomAnimationTimer() - { - if (!_npc.hasRandomAnimation() || _stopTask) - { - return; - } - - final int minWait = _npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION; - final int maxWait = _npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION; - - // Calculate the delay before the next animation - final int interval = Rnd.get(minWait, maxWait) * 1000; - - // Create a RandomAnimation Task that will be launched after the calculated delay - ThreadPool.schedule(this, interval); - } - - /** - * Stops the task from continuing and blocks it from continuing ever again. You need to create new task if you want to start it again. - */ - public void stopRandomAnimationTimer() - { - _stopTask = true; - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java new file mode 100644 index 0000000000..9cfff177ba --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/taskmanager/RandomAnimationManager.java @@ -0,0 +1,90 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.l2jmobius.gameserver.taskmanager; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; + +import org.l2jmobius.Config; +import org.l2jmobius.commons.concurrent.ThreadPool; +import org.l2jmobius.commons.util.Rnd; +import org.l2jmobius.gameserver.ai.CtrlIntention; +import org.l2jmobius.gameserver.model.actor.Npc; + +/** + * @author Mobius + */ +public class RandomAnimationManager +{ + private static final Map PENDING_ANIMATIONS = new ConcurrentHashMap<>(); + + public RandomAnimationManager() + { + ThreadPool.scheduleAtFixedRate(() -> + { + final long time = System.currentTimeMillis(); + for (Entry entry : PENDING_ANIMATIONS.entrySet()) + { + if (time > entry.getValue()) + { + final Npc npc = entry.getKey(); + if (!npc.isInActiveRegion()) + { + continue; + } + + // Cancel further animation schedules until intention is changed to ACTIVE again. + if (npc.isAttackable() && (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)) + { + continue; + } + + if (!npc.isDead() && !npc.hasBlockActions()) + { + npc.onRandomAnimation(Rnd.get(2, 3)); + } + + PENDING_ANIMATIONS.put(npc, time + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + }, 0, 1000); + } + + public void add(Npc npc) + { + if (npc.hasRandomAnimation()) + { + PENDING_ANIMATIONS.putIfAbsent(npc, System.currentTimeMillis() + (Rnd.get((npc.isAttackable() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION), (npc.isAttackable() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION)) * 1000)); + } + } + + public void remove(Npc npc) + { + PENDING_ANIMATIONS.remove(npc); + } + + public static RandomAnimationManager getInstance() + { + return SingletonHolder.INSTANCE; + } + + private static class SingletonHolder + { + protected static final RandomAnimationManager INSTANCE = new RandomAnimationManager(); + } +}