/*
* Copyright (C) 2004-2015 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server 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.
*
* L2J Server is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.gameserver.model.actor.status;
import com.l2jserver.Config;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.enums.PrivateStoreType;
import com.l2jserver.gameserver.instancemanager.DuelManager;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Playable;
import com.l2jserver.gameserver.model.actor.L2Summon;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.stat.PcStat;
import com.l2jserver.gameserver.model.entity.Duel;
import com.l2jserver.gameserver.model.stats.Formulas;
import com.l2jserver.gameserver.model.stats.Stats;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
public class PcStatus extends PlayableStatus
{
private double _currentCp = 0; // Current CP of the L2PcInstance
public PcStatus(L2PcInstance activeChar)
{
super(activeChar);
}
@Override
public final void reduceCp(int value)
{
if (getCurrentCp() > value)
{
setCurrentCp(getCurrentCp() - value);
}
else
{
setCurrentCp(0);
}
}
@Override
public final void reduceHp(double value, L2Character attacker)
{
reduceHp(value, attacker, true, false, false, false);
}
@Override
public final void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption)
{
reduceHp(value, attacker, awake, isDOT, isHPConsumption, false);
}
public final void reduceHp(double value, L2Character attacker, boolean awake, boolean isDOT, boolean isHPConsumption, boolean ignoreCP)
{
if (getActiveChar().isDead())
{
return;
}
// If OFFLINE_MODE_NO_DAMAGE is enabled and player is offline and he is in store/craft mode, no damage is taken.
if (Config.OFFLINE_MODE_NO_DAMAGE && (getActiveChar().getClient() != null) && getActiveChar().getClient().isDetached() && ((Config.OFFLINE_TRADE_ENABLE && ((getActiveChar().getPrivateStoreType() == PrivateStoreType.SELL) || (getActiveChar().getPrivateStoreType() == PrivateStoreType.BUY))) || (Config.OFFLINE_CRAFT_ENABLE && (getActiveChar().isInCraftMode() || (getActiveChar().getPrivateStoreType() == PrivateStoreType.MANUFACTURE)))))
{
return;
}
if (getActiveChar().isInvul() && !(isDOT || isHPConsumption))
{
return;
}
if (!isHPConsumption)
{
getActiveChar().stopEffectsOnDamage(awake);
// Attacked players in craft/shops stand up.
if (getActiveChar().isInCraftMode() || getActiveChar().isInStoreMode())
{
getActiveChar().setPrivateStoreType(PrivateStoreType.NONE);
getActiveChar().standUp();
getActiveChar().broadcastUserInfo();
}
else if (getActiveChar().isSitting())
{
getActiveChar().standUp();
}
if (!isDOT)
{
if (getActiveChar().isStunned() && (Rnd.get(10) == 0))
{
getActiveChar().stopStunning(true);
}
}
}
int fullValue = (int) value;
int tDmg = 0;
int mpDam = 0;
if ((attacker != null) && (attacker != getActiveChar()))
{
final L2PcInstance attackerPlayer = attacker.getActingPlayer();
if (attackerPlayer != null)
{
if (attackerPlayer.isGM() && !attackerPlayer.getAccessLevel().canGiveDamage())
{
return;
}
if (getActiveChar().isInDuel())
{
if (getActiveChar().getDuelState() == Duel.DUELSTATE_DEAD)
{
return;
}
else if (getActiveChar().getDuelState() == Duel.DUELSTATE_WINNER)
{
return;
}
// cancel duel if player got hit by another player, that is not part of the duel
if (attackerPlayer.getDuelId() != getActiveChar().getDuelId())
{
getActiveChar().setDuelState(Duel.DUELSTATE_INTERRUPTED);
}
}
}
// Check and calculate transfered damage
final L2Summon summon = getActiveChar().getAnyServitor();
if ((summon != null) && Util.checkIfInRange(1000, getActiveChar(), summon, true))
{
tDmg = ((int) value * (int) getActiveChar().getStat().calcStat(Stats.TRANSFER_DAMAGE_PERCENT, 0, null, null)) / 100;
// Only transfer dmg up to current HP, it should not be killed
tDmg = Math.min((int) summon.getCurrentHp() - 1, tDmg);
if (tDmg > 0)
{
summon.reduceCurrentHp(tDmg, attacker, null);
value -= tDmg;
fullValue = (int) value; // reduce the announced value here as player will get a message about summon damage
}
}
mpDam = ((int) value * (int) getActiveChar().getStat().calcStat(Stats.MANA_SHIELD_PERCENT, 0, null, null)) / 100;
if (mpDam > 0)
{
mpDam = (int) (value - mpDam);
if (mpDam > getActiveChar().getCurrentMp())
{
getActiveChar().sendPacket(SystemMessageId.MP_BECAME_0_AND_THE_ARCANE_SHIELD_IS_DISAPPEARING);
getActiveChar().stopSkillEffects(true, 1556);
value = mpDam - getActiveChar().getCurrentMp();
getActiveChar().setCurrentMp(0);
}
else
{
getActiveChar().reduceCurrentMp(mpDam);
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.ARCANE_SHIELD_DECREASED_YOUR_MP_BY_S1_INSTEAD_OF_HP);
smsg.addInt(mpDam);
getActiveChar().sendPacket(smsg);
return;
}
}
final L2PcInstance caster = getActiveChar().getTransferingDamageTo();
if ((caster != null) && (getActiveChar().getParty() != null) && Util.checkIfInRange(1000, getActiveChar(), caster, true) && !caster.isDead() && (getActiveChar() != caster) && getActiveChar().getParty().getMembers().contains(caster))
{
int transferDmg = 0;
transferDmg = ((int) value * (int) getActiveChar().getStat().calcStat(Stats.TRANSFER_DAMAGE_TO_PLAYER, 0, null, null)) / 100;
transferDmg = Math.min((int) caster.getCurrentHp() - 1, transferDmg);
if (transferDmg > 0)
{
int membersInRange = 0;
for (L2PcInstance member : caster.getParty().getMembers())
{
if (Util.checkIfInRange(1000, member, caster, false) && (member != caster))
{
membersInRange++;
}
}
if ((attacker instanceof L2Playable) && (caster.getCurrentCp() > 0))
{
if (caster.getCurrentCp() > transferDmg)
{
caster.getStatus().reduceCp(transferDmg);
}
else
{
transferDmg = (int) (transferDmg - caster.getCurrentCp());
caster.getStatus().reduceCp((int) caster.getCurrentCp());
}
}
if (membersInRange > 0)
{
caster.reduceCurrentHp(transferDmg / membersInRange, attacker, null);
value -= transferDmg;
fullValue = (int) value;
}
}
}
if (!ignoreCP && (attacker instanceof L2Playable))
{
if (getCurrentCp() >= value)
{
setCurrentCp(getCurrentCp() - value); // Set Cp to diff of Cp vs value
value = 0; // No need to subtract anything from Hp
}
else
{
value -= getCurrentCp(); // Get diff from value vs Cp; will apply diff to Hp
setCurrentCp(0, false); // Set Cp to 0
}
}
if ((fullValue > 0) && !isDOT)
{
SystemMessage smsg;
// Send a System Message to the L2PcInstance
smsg = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_RECEIVED_S3_DAMAGE_FROM_C2);
smsg.addString(getActiveChar().getName());
smsg.addCharName(attacker);
smsg.addInt(fullValue);
getActiveChar().sendPacket(smsg);
if ((tDmg > 0) && (summon != null))
{
smsg = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_RECEIVED_S3_DAMAGE_FROM_C2);
smsg.addString(summon.getName());
smsg.addCharName(attacker);
smsg.addInt(tDmg);
getActiveChar().sendPacket(smsg);
if (attackerPlayer != null)
{
smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_DEALT_S1_DAMAGE_TO_YOUR_TARGET_AND_S2_DAMAGE_TO_THE_SERVITOR);
smsg.addInt(fullValue);
smsg.addInt(tDmg);
attackerPlayer.sendPacket(smsg);
}
}
}
}
if (value > 0)
{
value = getCurrentHp() - value;
if (value <= 0)
{
if (getActiveChar().isInDuel())
{
getActiveChar().disableAllSkills();
stopHpMpRegeneration();
if (attacker != null)
{
attacker.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
attacker.sendPacket(ActionFailed.STATIC_PACKET);
}
// let the DuelManager know of his defeat
DuelManager.getInstance().onPlayerDefeat(getActiveChar());
value = 1;
}
else
{
value = 0;
}
}
setCurrentHp(value);
}
if (getActiveChar().getCurrentHp() < 0.5)
{
getActiveChar().abortAttack();
getActiveChar().abortCast();
if (getActiveChar().isInOlympiadMode())
{
stopHpMpRegeneration();
getActiveChar().setIsDead(true);
getActiveChar().setIsPendingRevive(true);
final L2Summon pet = getActiveChar().getPet();
if (pet != null)
{
pet.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null);
}
getActiveChar().getServitors().values().forEach(s -> s.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null));
return;
}
getActiveChar().doDie(attacker);
}
}
@Override
public final double getCurrentCp()
{
return _currentCp;
}
@Override
public final void setCurrentCp(double newCp)
{
setCurrentCp(newCp, true);
}
public final void setCurrentCp(double newCp, boolean broadcastPacket)
{
// Get the Max CP of the L2Character
int currentCp = (int) getCurrentCp();
int maxCp = getActiveChar().getStat().getMaxCp();
synchronized (this)
{
if (getActiveChar().isDead())
{
return;
}
if (newCp < 0)
{
newCp = 0;
}
if (newCp >= maxCp)
{
// Set the RegenActive flag to false
_currentCp = maxCp;
_flagsRegenActive &= ~REGEN_FLAG_CP;
// Stop the HP/MP/CP Regeneration task
if (_flagsRegenActive == 0)
{
stopHpMpRegeneration();
}
}
else
{
// Set the RegenActive flag to true
_currentCp = newCp;
_flagsRegenActive |= REGEN_FLAG_CP;
// 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 ((currentCp != _currentCp) && broadcastPacket)
{
getActiveChar().broadcastStatusUpdate();
}
}
@Override
protected void doRegeneration()
{
final PcStat charstat = getActiveChar().getStat();
// Modify the current CP of the L2Character and broadcast Server->Client packet StatusUpdate
if (getCurrentCp() < charstat.getMaxRecoverableCp())
{
setCurrentCp(getCurrentCp() + Formulas.calcCpRegen(getActiveChar()), false);
}
// Modify the current HP of the L2Character and broadcast Server->Client packet StatusUpdate
if (getCurrentHp() < charstat.getMaxRecoverableHp())
{
setCurrentHp(getCurrentHp() + Formulas.calcHpRegen(getActiveChar()), false);
}
// Modify the current MP of the L2Character and broadcast Server->Client packet StatusUpdate
if (getCurrentMp() < charstat.getMaxRecoverableMp())
{
setCurrentMp(getCurrentMp() + Formulas.calcMpRegen(getActiveChar()), false);
}
getActiveChar().broadcastStatusUpdate(); // send the StatusUpdate packet
}
@Override
public L2PcInstance getActiveChar()
{
return (L2PcInstance) super.getActiveChar();
}
}