Java tutorial
/* * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If * not, see <http://www.gnu.org/licenses/>. */ package silentium.gameserver.utils; import java.util.Iterator; import java.util.List; import java.util.Set; import javolution.util.FastSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import silentium.commons.utils.Rnd; import silentium.gameserver.ThreadPoolManager; import silentium.gameserver.configs.NPCConfig; import silentium.gameserver.idfactory.IdFactory; import silentium.gameserver.model.L2MinionData; import silentium.gameserver.model.actor.L2Character; import silentium.gameserver.model.actor.instance.L2MonsterInstance; import silentium.gameserver.tables.NpcTable; import silentium.gameserver.templates.chars.L2NpcTemplate; import com.google.common.collect.Lists; /** * @author luisantonioa, DS */ public class MinionList { private static Logger _log = LoggerFactory.getLogger(MinionList.class.getName()); protected final L2MonsterInstance _master; private final List<L2MonsterInstance> _minionReferences; protected List<L2MonsterInstance> _reusedMinionReferences = null; public MinionList(L2MonsterInstance pMaster) { if (pMaster == null) throw new NullPointerException("MinionList: master is null"); _master = pMaster; _minionReferences = Lists.newCopyOnWriteArrayList(); } /** * @return list of the spawned (alive) minions. */ public List<L2MonsterInstance> getSpawnedMinions() { return _minionReferences; } /** * Manage the spawn of Minions.<BR> * <BR> * <B><U> Actions</U> :</B><BR> * <BR> * <li>Get the Minion data of all Minions that must be spawn</li> <li>For each Minion type, spawn the amount of Minion needed</li> <BR> * <BR> */ public final void spawnMinions() { if (_master.isAlikeDead()) return; List<L2MinionData> minions = _master.getTemplate().getMinionData(); if (minions == null) return; int minionCount, minionId, minionsToSpawn; for (L2MinionData minion : minions) { minionCount = minion.getAmount(); minionId = minion.getMinionId(); minionsToSpawn = minionCount - countSpawnedMinionsById(minionId); if (minionsToSpawn > 0) { for (int i = 0; i < minionsToSpawn; i++) spawnMinion(minionId); } } // remove non-needed minions deleteReusedMinions(); } /** * Delete all spawned minions and try to reuse them. */ public void deleteSpawnedMinions() { if (!_minionReferences.isEmpty()) { for (L2MonsterInstance minion : _minionReferences) { if (minion != null) { minion.setLeader(null); minion.deleteMe(); if (_reusedMinionReferences != null) _reusedMinionReferences.add(minion); } } _minionReferences.clear(); } } /** * Delete all reused minions to prevent memory leaks. */ public void deleteReusedMinions() { if (_reusedMinionReferences != null) _reusedMinionReferences.clear(); } // hooks /** * Called on the master spawn Old minions (from previous spawn) are deleted. If master can respawn - enabled reuse of the killed minions. */ public void onMasterSpawn() { deleteSpawnedMinions(); // if master has spawn and can respawn - try to reuse minions if (_reusedMinionReferences == null && _master.getTemplate().getMinionData() != null && _master.getSpawn() != null && _master.getSpawn().isRespawnEnabled()) _reusedMinionReferences = Lists.newCopyOnWriteArrayList(); } /** * Called on the minion spawn and added them in the list of the spawned minions. * * @param minion * The instance of minion. */ public void onMinionSpawn(L2MonsterInstance minion) { _minionReferences.add(minion); } /** * Called on the master death/delete. * * @param force * if true - force delete of the spawned minions By default minions deleted only for raidbosses */ public void onMasterDie(boolean force) { if (_master.isRaid() || force) deleteSpawnedMinions(); } /** * Called on the minion death/delete. Removed minion from the list of the spawned minions and reuse if possible. * * @param minion * The minion to make checks on. * @param respawnTime * (ms) enable respawning of this minion while master is alive. -1 - use default value: 0 (disable) for mobs and config value for * raids. */ public void onMinionDie(L2MonsterInstance minion, int respawnTime) { minion.setLeader(null); // prevent memory leaks _minionReferences.remove(minion); if (_reusedMinionReferences != null) _reusedMinionReferences.add(minion); final int time = respawnTime < 0 ? _master.isRaid() ? (int) NPCConfig.RAID_MINION_RESPAWN_TIMER : 0 : respawnTime; if (time > 0 && !_master.isAlikeDead()) ThreadPoolManager.getInstance().scheduleGeneral(new MinionRespawnTask(minion), time); } /** * Called if master/minion was attacked. Master and all free minions receive aggro against attacker. * * @param caller * That instance will call for help versus attacker. * @param attacker * That instance will receive all aggro. */ public void onAssist(L2Character caller, L2Character attacker) { if (attacker == null) return; if (!_master.isAlikeDead() && !_master.isInCombat()) _master.addDamageHate(attacker, 0, 1); final boolean callerIsMaster = caller == _master; int aggro = callerIsMaster ? 10 : 1; if (_master.isRaid()) aggro *= 10; for (L2MonsterInstance minion : _minionReferences) { if (minion != null && !minion.isDead() && (callerIsMaster || !minion.isInCombat())) minion.addDamageHate(attacker, 0, aggro); } } /** * Called from onTeleported() of the master Alive and able to move minions teleported to master. */ public void onMasterTeleported() { final int offset = 200; final int minRadius = _master.getCollisionRadius() + 30; for (L2MonsterInstance minion : _minionReferences) { if (minion != null && !minion.isDead() && !minion.isMovementDisabled()) { int newX = Rnd.get(minRadius * 2, offset * 2); // x int newY = Rnd.get(newX, offset * 2); // distance newY = (int) Math.sqrt(newY * newY - newX * newX); // y if (newX > offset + minRadius) newX = _master.getX() + newX - offset; else newX = _master.getX() - newX + minRadius; if (newY > offset + minRadius) newY = _master.getY() + newY - offset; else newY = _master.getY() - newY + minRadius; minion.teleToLocation(newX, newY, _master.getZ()); } } } private final void spawnMinion(int minionId) { if (minionId == 0) return; // searching in reused minions if (_reusedMinionReferences != null && !_reusedMinionReferences.isEmpty()) { L2MonsterInstance minion; Iterator<L2MonsterInstance> iter = _reusedMinionReferences.iterator(); while (iter.hasNext()) { minion = iter.next(); if (minion != null && minion.getNpcId() == minionId) { iter.remove(); minion.refreshID(); initializeNpcInstance(_master, minion); return; } } } // not found in cache spawnMinion(_master, minionId); } private final class MinionRespawnTask implements Runnable { private final L2MonsterInstance _minion; public MinionRespawnTask(L2MonsterInstance minion) { _minion = minion; } @Override public void run() { if (!_master.isAlikeDead() && _master.isVisible()) { // minion can be already spawned or deleted if (!_minion.isVisible()) { if (_reusedMinionReferences != null) _reusedMinionReferences.remove(_minion); _minion.refreshID(); initializeNpcInstance(_master, _minion); } } } } /** * Init a Minion and add it in the world as a visible object.<BR> * <BR> * <B><U> Actions</U> :</B><BR> * <BR> * <li>Get the template of the Minion to spawn</li> <li>Create and Init the Minion and generate its Identifier</li> <li>Set the Minion HP, MP * and Heading</li> <li>Set the Minion leader to this RaidBoss</li> <li>Init the position of the Minion and add it in the world as a visible * object</li><BR> * <BR> * * @param master * L2MonsterInstance used as master for this minion * @param minionId * The L2NpcTemplate Identifier of the Minion to spawn * @return the instance of the new minion. */ public static final L2MonsterInstance spawnMinion(L2MonsterInstance master, int minionId) { // Get the template of the Minion to spawn L2NpcTemplate minionTemplate = NpcTable.getInstance().getTemplate(minionId); if (minionTemplate == null) return null; // Create and Init the Minion and generate its Identifier L2MonsterInstance minion = new L2MonsterInstance(IdFactory.getInstance().getNextId(), minionTemplate); return initializeNpcInstance(master, minion); } protected static final L2MonsterInstance initializeNpcInstance(L2MonsterInstance master, L2MonsterInstance minion) { minion.stopAllEffects(); minion.setIsDead(false); minion.setDecayed(false); // Set the Minion HP, MP and Heading minion.setCurrentHpMp(minion.getMaxHp(), minion.getMaxMp()); minion.setHeading(master.getHeading()); // Set the Minion leader to this RaidBoss minion.setLeader(master); // Init the position of the Minion and add it in the world as a visible object final int offset = 200; final int minRadius = master.getCollisionRadius() + 30; int newX = Rnd.get(minRadius * 2, offset * 2); // x int newY = Rnd.get(newX, offset * 2); // distance newY = (int) Math.sqrt(newY * newY - newX * newX); // y if (newX > offset + minRadius) newX = master.getX() + newX - offset; else newX = master.getX() - newX + minRadius; if (newY > offset + minRadius) newY = master.getY() + newY - offset; else newY = master.getY() - newY + minRadius; minion.spawnMe(newX, newY, master.getZ()); _log.trace("Spawned minion template " + minion.getNpcId() + " with objid: " + minion.getObjectId() + " to " + "boss " + master.getObjectId() + " ,at: " + minion.getX() + " x, " + minion.getY() + " y, " + minion.getZ() + " z"); return minion; } private final int countSpawnedMinionsById(int minionId) { int count = 0; for (L2MonsterInstance minion : _minionReferences) { if (minion != null && minion.getNpcId() == minionId) count++; } return count; } public final int countSpawnedMinions() { return _minionReferences.size(); } public final int lazyCountSpawnedMinionsGroups() { Set<Integer> seenGroups = new FastSet<>(); for (L2MonsterInstance minion : _minionReferences) { if (minion == null) continue; seenGroups.add(minion.getNpcId()); } return seenGroups.size(); } }