silentium.gameserver.model.actor.status.CharStatus.java Source code

Java tutorial

Introduction

Here is the source code for silentium.gameserver.model.actor.status.CharStatus.java

Source

/*
 * 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;
    }
}