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.model.actor.status; import java.util.Set; import java.util.concurrent.Future; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import silentium.commons.utils.Rnd; import silentium.gameserver.ThreadPoolManager; import silentium.gameserver.model.actor.L2Character; import silentium.gameserver.model.actor.instance.L2PcInstance; import silentium.gameserver.model.actor.stat.CharStat; import silentium.gameserver.skills.Formulas; import com.google.common.collect.Sets; public class CharStatus { protected static final Logger _log = LoggerFactory.getLogger(CharStatus.class.getName()); private final L2Character _activeChar; private double _currentHp = 0; // Current HP of the L2Character private double _currentMp = 0; // Current MP of the L2Character /** Array containing all clients that need to be notified about hp/mp updates of the L2Character */ private Set<L2Character> _StatusListener; private Future<?> _regTask; protected byte _flagsRegenActive = 0; protected static final byte REGEN_FLAG_CP = 4; private static final byte REGEN_FLAG_HP = 1; private static final byte REGEN_FLAG_MP = 2; public CharStatus(L2Character activeChar) { _activeChar = activeChar; } /** * Add the object to the list of L2Character that must be informed of HP/MP updates of this L2Character.<BR> * <BR> * <B><U> Concept</U> :</B><BR> * <BR> * Each L2Character owns a list called <B>_statusListener</B> that contains all L2PcInstance to inform of HP/MP updates. Players who must be * informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send * Server->Client packet StatusUpdate.<BR> * <BR> * <B><U> Example of use </U> :</B><BR> * <BR> * <li>Target a PC or NPC</li><BR> * <BR> * * @param object * L2Character to add to the listener */ public final void addStatusListener(L2Character object) { if (object == getActiveChar()) return; getStatusListener().add(object); } /** * Remove the object from the list of L2Character that must be informed of HP/MP updates of this L2Character.<BR> * <BR> * <B><U> Concept</U> :</B><BR> * <BR> * Each L2Character owns a list called <B>_statusListener</B> that contains all L2PcInstance to inform of HP/MP updates. Players who must be * informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send * Server->Client packet StatusUpdate.<BR> * <BR> * <B><U> Example of use </U> :</B><BR> * <BR> * <li>Untarget a PC or NPC</li><BR> * <BR> * * @param object * L2Character to add to the listener */ public final void removeStatusListener(L2Character object) { getStatusListener().remove(object); } /** * Return the list of L2Character that must be informed of HP/MP updates of this L2Character.<BR> * <BR> * <B><U> Concept</U> :</B><BR> * <BR> * Each L2Character owns a list called <B>_statusListener</B> that contains all L2PcInstance to inform of HP/MP updates. Players who must be * informed are players that target this L2Character. When a RegenTask is in progress sever just need to go through this list to send * Server->Client packet StatusUpdate.<BR> * <BR> * * @return The list of L2Character to inform or null if empty */ public final Set<L2Character> getStatusListener() { if (_StatusListener == null) _StatusListener = Sets.newCopyOnWriteArraySet(); return _StatusListener; } // place holder, only PcStatus has CP public void reduceCp(int value) { } /** * Reduce the current HP of the L2Character and launch the doDie Task if necessary.<BR> * <BR> * <B><U> Overriden in </U> :</B><BR> * <BR> * <li>L2Attackable : Update the attacker AggroInfo of the L2Attackable _aggroList</li><BR> * <BR> * * @param value * The amount of removed HPs. * @param attacker * The L2Character who attacks */ public void reduceHp(double value, L2Character attacker) { reduceHp(value, attacker, true, false, false); } public void reduceHp(double value, L2Character attacker, boolean isHpConsumption) { reduceHp(value, attacker, true, false, isHpConsumption); } public void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption) { if (getActiveChar().isDead()) return; // invul handling if (getActiveChar().isInvul()) { // other chars can't damage if (attacker != getActiveChar()) return; // only DOT and HP consumption allowed for damage self if (!isDOT && !isHPConsumption) return; } if (attacker != null) { final L2PcInstance attackerPlayer = attacker.getActingPlayer(); if (attackerPlayer != null && attackerPlayer.isGM() && !attackerPlayer.getAccessLevel().canGiveDamage()) return; } if (!isDOT && !isHPConsumption) { getActiveChar().stopEffectsOnDamage(awake); if (getActiveChar().isStunned() && Rnd.get(10) == 0) getActiveChar().stopStunning(true); if (getActiveChar().isImmobileUntilAttacked()) getActiveChar().stopImmobileUntilAttacked(null); } if (value > 0) // Reduce Hp if any setCurrentHp(Math.max(getCurrentHp() - value, 0)); // Die if character is mortal if (getActiveChar().getCurrentHp() < 0.5 && getActiveChar().isMortal()) { getActiveChar().abortAttack(); getActiveChar().abortCast(); getActiveChar().doDie(attacker); } } public void reduceMp(double value) { setCurrentMp(Math.max(getCurrentMp() - value, 0)); } /** * Start the HP/MP/CP Regeneration task.<BR> * <BR> * <B><U> Actions</U> :</B><BR> * <BR> * <li>Calculate the regen task period</li> <li>Launch the HP/MP/CP Regeneration task with Medium priority</li><BR> * <BR> */ public final synchronized void startHpMpRegeneration() { if (_regTask == null && !getActiveChar().isDead()) { _log.trace("HP/MP regen started"); // Get the Regeneration periode int period = Formulas.getRegeneratePeriod(getActiveChar()); // Create the HP/MP/CP Regeneration task _regTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new RegenTask(), period, period); } } /** * Stop the HP/MP/CP Regeneration task.<BR> * <BR> * <B><U> Actions</U> :</B><BR> * <BR> * <li>Set the RegenActive flag to False</li> <li>Stop the HP/MP/CP Regeneration task</li> */ public final synchronized void stopHpMpRegeneration() { if (_regTask != null) { _log.trace("HP/MP regen stop"); // Stop the HP/MP/CP Regeneration task _regTask.cancel(false); _regTask = null; // Set the RegenActive flag to false _flagsRegenActive = 0; } } // place holder, only PcStatus has CP public double getCurrentCp() { return 0; } // place holder, only PcStatus has CP public void setCurrentCp(double newCp) { } public final double getCurrentHp() { return _currentHp; } public final void setCurrentHp(double newHp) { setCurrentHp(newHp, true); } public void setCurrentHp(double newHp, boolean broadcastPacket) { // Get the Max HP of the L2Character final double maxHp = getActiveChar().getStat().getMaxHp(); synchronized (this) { if (getActiveChar().isDead()) return; if (newHp >= maxHp) { // Set the RegenActive flag to false _currentHp = maxHp; _flagsRegenActive &= ~REGEN_FLAG_HP; // Stop the HP/MP/CP Regeneration task if (_flagsRegenActive == 0) stopHpMpRegeneration(); } else { // Set the RegenActive flag to true _currentHp = newHp; _flagsRegenActive |= REGEN_FLAG_HP; // Start the HP/MP/CP Regeneration task with Medium priority startHpMpRegeneration(); } } // Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform if (broadcastPacket) getActiveChar().broadcastStatusUpdate(); } public final void setCurrentHpMp(double newHp, double newMp) { setCurrentHp(newHp, false); setCurrentMp(newMp, true); // send the StatusUpdate only once } public final double getCurrentMp() { return _currentMp; } public final void setCurrentMp(double newMp) { setCurrentMp(newMp, true); } public final void setCurrentMp(double newMp, boolean broadcastPacket) { // Get the Max MP of the L2Character final int maxMp = getActiveChar().getStat().getMaxMp(); synchronized (this) { if (getActiveChar().isDead()) return; if (newMp >= maxMp) { // Set the RegenActive flag to false _currentMp = maxMp; _flagsRegenActive &= ~REGEN_FLAG_MP; // Stop the HP/MP/CP Regeneration task if (_flagsRegenActive == 0) stopHpMpRegeneration(); } else { // Set the RegenActive flag to true _currentMp = newMp; _flagsRegenActive |= REGEN_FLAG_MP; // Start the HP/MP/CP Regeneration task with Medium priority startHpMpRegeneration(); } } // Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform if (broadcastPacket) getActiveChar().broadcastStatusUpdate(); } protected void doRegeneration() { final CharStat charstat = getActiveChar().getStat(); // Modify the current HP of the L2Character and broadcast Server->Client packet StatusUpdate if (getCurrentHp() < charstat.getMaxHp()) setCurrentHp(getCurrentHp() + Formulas.calcHpRegen(getActiveChar()), false); // Modify the current MP of the L2Character and broadcast Server->Client packet StatusUpdate if (getCurrentMp() < charstat.getMaxMp()) setCurrentMp(getCurrentMp() + Formulas.calcMpRegen(getActiveChar()), false); if (!getActiveChar().isInActiveRegion()) { // no broadcast necessary for characters that are in inactive regions. // stop regeneration for characters who are filled up and in an inactive region. if ((getCurrentHp() == charstat.getMaxHp()) && (getCurrentMp() == charstat.getMaxMp())) stopHpMpRegeneration(); } else getActiveChar().broadcastStatusUpdate(); // send the StatusUpdate packet } /** Task of HP/MP regeneration */ class RegenTask implements Runnable { @Override public void run() { try { doRegeneration(); } catch (Exception e) { _log.error("", e); } } } public L2Character getActiveChar() { return _activeChar; } }