/*
* 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.instance;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.enums.InstanceType;
import com.l2jserver.gameserver.enums.TrapAction;
import com.l2jserver.gameserver.model.actor.L2Attackable;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.knownlist.TrapKnownList;
import com.l2jserver.gameserver.model.actor.tasks.npc.trap.TrapTask;
import com.l2jserver.gameserver.model.actor.tasks.npc.trap.TrapTriggerTask;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.events.EventDispatcher;
import com.l2jserver.gameserver.model.events.impl.character.trap.OnTrapAction;
import com.l2jserver.gameserver.model.holders.SkillHolder;
import com.l2jserver.gameserver.model.items.L2Weapon;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.olympiad.OlympiadGameManager;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.zone.ZoneId;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo.TrapInfo;
import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
/**
* Trap instance.
* @author Zoey76
*/
public final class L2TrapInstance extends L2Npc
{
private static final int TICK = 1000; // 1s
private boolean _hasLifeTime;
private boolean _isInArena = false;
private boolean _isTriggered;
private final int _lifeTime;
private L2PcInstance _owner;
private final List<Integer> _playersWhoDetectedMe = new ArrayList<>();
private final SkillHolder _skill;
private int _remainingTime;
// Tasks
private ScheduledFuture<?> _trapTask = null;
/**
* Creates a trap.
* @param template the trap NPC template
* @param instanceId the instance ID
* @param lifeTime the life time
*/
public L2TrapInstance(L2NpcTemplate template, int instanceId, int lifeTime)
{
super(template);
setInstanceType(InstanceType.L2TrapInstance);
setInstanceId(instanceId);
setName(template.getName());
setIsInvul(false);
_owner = null;
_isTriggered = false;
_skill = getTemplate().getParameters().getObject("trap_skill", SkillHolder.class);
_hasLifeTime = lifeTime >= 0;
_lifeTime = lifeTime != 0 ? lifeTime : 30000;
_remainingTime = _lifeTime;
if (_skill != null)
{
_trapTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new TrapTask(this), TICK, TICK);
}
}
/**
* Creates a trap.
* @param template the trap NPC template
* @param owner the owner
* @param lifeTime the life time
*/
public L2TrapInstance(L2NpcTemplate template, L2PcInstance owner, int lifeTime)
{
this(template, owner.getInstanceId(), lifeTime);
_owner = owner;
}
@Override
public void broadcastPacket(L2GameServerPacket mov)
{
for (L2PcInstance player : getKnownList().getKnownPlayers().values())
{
if ((player != null) && (_isTriggered || canBeSeen(player)))
{
player.sendPacket(mov);
}
}
}
@Override
public void broadcastPacket(L2GameServerPacket mov, int radiusInKnownlist)
{
for (L2PcInstance player : getKnownList().getKnownPlayers().values())
{
if ((player != null) && isInsideRadius(player, radiusInKnownlist, false, false) && (_isTriggered || canBeSeen(player)))
{
player.sendPacket(mov);
}
}
}
/**
* Verify if the character can see the trap.
* @param cha the character to verify
* @return {@code true} if the character can see the trap, {@code false} otherwise
*/
public boolean canBeSeen(L2Character cha)
{
if ((cha != null) && _playersWhoDetectedMe.contains(cha.getObjectId()))
{
return true;
}
if ((_owner == null) || (cha == null))
{
return false;
}
if (cha == _owner)
{
return true;
}
if (cha instanceof L2PcInstance)
{
// observers can't see trap
if (((L2PcInstance) cha).inObserverMode())
{
return false;
}
// olympiad competitors can't see trap
if (_owner.isInOlympiadMode() && ((L2PcInstance) cha).isInOlympiadMode() && (((L2PcInstance) cha).getOlympiadSide() != _owner.getOlympiadSide()))
{
return false;
}
}
if (_isInArena)
{
return true;
}
if (_owner.isInParty() && cha.isInParty() && (_owner.getParty().getLeaderObjectId() == cha.getParty().getLeaderObjectId()))
{
return true;
}
return false;
}
public boolean checkTarget(L2Character target)
{
if (!Skill.checkForAreaOffensiveSkills(this, target, _skill.getSkill(), _isInArena))
{
return false;
}
if (!target.isInsideRadius(this, _skill.getSkill().getEffectRange(), false, false))
{
return false;
}
// observers
if ((target instanceof L2PcInstance) && ((L2PcInstance) target).inObserverMode())
{
return false;
}
// olympiad own team and their summons not attacked
if ((_owner != null) && _owner.isInOlympiadMode())
{
final L2PcInstance player = target.getActingPlayer();
if ((player != null) && player.isInOlympiadMode() && (player.getOlympiadSide() == _owner.getOlympiadSide()))
{
return false;
}
}
if (_isInArena)
{
return true;
}
// trap owned by players not attack non-flagged players
if (_owner != null)
{
if (target instanceof L2Attackable)
{
return true;
}
final L2PcInstance player = target.getActingPlayer();
if ((player == null) || ((player.getPvpFlag() == 0) && (player.getKarma() == 0)))
{
return false;
}
}
return true;
}
@Override
public boolean deleteMe()
{
if (_owner != null)
{
_owner.setTrap(null);
_owner = null;
}
return super.deleteMe();
}
@Override
public L2PcInstance getActingPlayer()
{
return _owner;
}
@Override
public L2Weapon getActiveWeaponItem()
{
return null;
}
public int getKarma()
{
return _owner != null ? _owner.getKarma() : 0;
}
@Override
public TrapKnownList getKnownList()
{
return (TrapKnownList) super.getKnownList();
}
/**
* Get the owner of this trap.
* @return the owner
*/
public L2PcInstance getOwner()
{
return _owner;
}
public byte getPvpFlag()
{
return _owner != null ? _owner.getPvpFlag() : 0;
}
@Override
public L2ItemInstance getSecondaryWeaponInstance()
{
return null;
}
@Override
public L2Weapon getSecondaryWeaponItem()
{
return null;
}
public Skill getSkill()
{
return _skill.getSkill();
}
@Override
public void initKnownList()
{
setKnownList(new TrapKnownList(this));
}
@Override
public boolean isAutoAttackable(L2Character attacker)
{
return !canBeSeen(attacker);
}
@Override
public boolean isTrap()
{
return true;
}
/**
* Checks is triggered
* @return True if trap is triggered.
*/
public boolean isTriggered()
{
return _isTriggered;
}
@Override
public void onSpawn()
{
super.onSpawn();
_isInArena = isInsideZone(ZoneId.PVP) && !isInsideZone(ZoneId.SIEGE);
_playersWhoDetectedMe.clear();
}
@Override
public void sendDamageMessage(L2Character target, int damage, boolean mcrit, boolean pcrit, boolean miss)
{
if (miss || (_owner == null))
{
return;
}
if (_owner.isInOlympiadMode() && (target instanceof L2PcInstance) && ((L2PcInstance) target).isInOlympiadMode() && (((L2PcInstance) target).getOlympiadGameId() == _owner.getOlympiadGameId()))
{
OlympiadGameManager.getInstance().notifyCompetitorDamage(getOwner(), damage);
}
if (target.isInvul() && !(target instanceof L2NpcInstance))
{
_owner.sendPacket(SystemMessageId.THE_ATTACK_HAS_BEEN_BLOCKED);
}
else
{
final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_INFLICTED_S3_DAMAGE_ON_C2_S4);
sm.addCharName(this);
sm.addCharName(target);
sm.addInt(damage);
sm.addPopup(target.getObjectId(), getObjectId(), (damage * -1));
_owner.sendPacket(sm);
}
}
@Override
public void sendInfo(L2PcInstance activeChar)
{
if (_isTriggered || canBeSeen(activeChar))
{
activeChar.sendPacket(new TrapInfo(this, activeChar));
}
}
public void setDetected(L2Character detector)
{
if (_isInArena)
{
if (detector.isPlayable())
{
sendInfo(detector.getActingPlayer());
}
return;
}
if ((_owner != null) && (_owner.getPvpFlag() == 0) && (_owner.getKarma() == 0))
{
return;
}
_playersWhoDetectedMe.add(detector.getObjectId());
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnTrapAction(this, detector, TrapAction.TRAP_DETECTED), this);
if (detector.isPlayable())
{
sendInfo(detector.getActingPlayer());
}
}
public void stopDecay()
{
DecayTaskManager.getInstance().cancel(this);
}
/**
* Trigger the trap.
* @param target the target
*/
public void triggerTrap(L2Character target)
{
if (_trapTask != null)
{
_trapTask.cancel(true);
_trapTask = null;
}
_isTriggered = true;
broadcastPacket(new TrapInfo(this, null));
setTarget(target);
EventDispatcher.getInstance().notifyEventAsync(new OnTrapAction(this, target, TrapAction.TRAP_TRIGGERED), this);
ThreadPoolManager.getInstance().scheduleGeneral(new TrapTriggerTask(this), 300);
}
public void unSummon()
{
if (_trapTask != null)
{
_trapTask.cancel(true);
_trapTask = null;
}
if (_owner != null)
{
_owner.setTrap(null);
_owner = null;
}
if (isVisible() && !isDead())
{
if (getWorldRegion() != null)
{
getWorldRegion().removeFromZones(this);
}
deleteMe();
}
}
@Override
public void updateAbnormalVisualEffects()
{
}
public boolean hasLifeTime()
{
return _hasLifeTime;
}
public void setHasLifeTime(boolean val)
{
_hasLifeTime = val;
}
public int getRemainingTime()
{
return _remainingTime;
}
public void setRemainingTime(int time)
{
_remainingTime = time;
}
public int getLifeTime()
{
return _lifeTime;
}
}