/*
* This file is part of aion-unique <aion-unique.com>.
*
* aion-unique 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.
*
* aion-unique 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 aion-unique. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aionemu.gameserver.skillengine.model;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.aionemu.gameserver.controllers.movement.StartMovingListener;
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.stats.StatEnum;
import com.aionemu.gameserver.model.templates.item.ItemTemplate;
import com.aionemu.gameserver.network.aion.serverpackets.SM_CASTSPELL;
import com.aionemu.gameserver.network.aion.serverpackets.SM_CASTSPELL_END;
import com.aionemu.gameserver.restrictions.RestrictionsManager;
import com.aionemu.gameserver.skillengine.SkillEngine;
import com.aionemu.gameserver.skillengine.action.Action;
import com.aionemu.gameserver.skillengine.action.Actions;
import com.aionemu.gameserver.skillengine.condition.Condition;
import com.aionemu.gameserver.skillengine.condition.Conditions;
import com.aionemu.gameserver.skillengine.effect.EffectId;
import com.aionemu.gameserver.skillengine.properties.Properties;
import com.aionemu.gameserver.skillengine.properties.Property;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.ThreadPoolManager;
/**
* @author ATracer
*
*/
public class Skill
{
private List<Creature> effectedList;
private Creature firstTarget;
private Creature effector;
private int skillLevel;
private int skillStackLvl;
private StartMovingListener conditionChangeListener;
private SkillTemplate skillTemplate;
private boolean firstTargetRangeCheck = true;
private ItemTemplate itemTemplate;
/**
* Duration that depends on BOOST_CASTING_TIME
*/
private int duration;
public enum SkillType
{
CAST,
ITEM,
PASSIVE
}
/**
* Each skill is a separate object upon invocation
* Skill level will be populated from player SkillList
*
* @param skillTemplate
* @param effector
* @param world
*/
public Skill(SkillTemplate skillTemplate, Player effector, Creature firstTarget)
{
this(skillTemplate, effector,
effector.getSkillList().getSkillLevel(skillTemplate.getSkillId()), firstTarget);
}
/**
*
* @param skillTemplate
* @param effector
* @param skillLvl
* @param firstTarget
*/
public Skill(SkillTemplate skillTemplate, Creature effector, int skillLvl, Creature firstTarget) {
this.effectedList = new ArrayList<Creature>();
this.conditionChangeListener = new StartMovingListener();
this.firstTarget = firstTarget;
this.skillLevel = skillLvl;
this.skillStackLvl = skillTemplate.getLvl();
this.skillTemplate = skillTemplate;
this.effector = effector;
}
/**
* Skill entry point
*/
public void useSkill()
{
if(!skillTemplate.isActive()
&& skillTemplate.isPassive()
&& skillTemplate.isProvoked()
&& skillTemplate.isToggle())
return;
if(!setProperties(skillTemplate.getInitproperties()))
return;
if(!preCastCheck())
return;
if(!setProperties(skillTemplate.getSetproperties()))
return;
//start casting
effector.setCasting(this);
Iterator<Creature> effectedIter = effectedList.iterator();
while(effectedIter.hasNext())
{
Creature effected = effectedIter.next();
if(effector instanceof Player)
{
if (!RestrictionsManager.canAffectBySkill((Player)effector, effected))
effectedIter.remove();
}
else
{
if(effector.getEffectController().isAbnormalState(EffectId.CANT_ATTACK_STATE))
effectedIter.remove();
}
}
if(effectedList.size() == 0)
{
effector.setCasting(null);
return;
}
int skillDuration = skillTemplate.getDuration();
int currentStat = effector.getGameStats().getCurrentStat(StatEnum.BOOST_CASTING_TIME);
this.duration = skillDuration - Math.round(skillDuration * (currentStat - 100) / 100f);
if(duration < 0)
duration = 0;
//temporary hook till i find permanent solution
if(skillTemplate.isActive() || skillTemplate.isToggle())
{
startCast();
}
effector.getObserveController().attach(conditionChangeListener);
if(this.duration > 0)
{
schedule(this.duration);
}
else
{
endCast();
}
}
/**
* Penalty success skill
*/
private void startPenaltySkill()
{
if(skillTemplate.getPenaltySkillId() == 0)
return;
Skill skill = SkillEngine.getInstance().getSkill(effector, skillTemplate.getPenaltySkillId(), 1, firstTarget);
skill.useSkill();
}
/**
* Start casting of skill
*/
private void startCast()
{
int targetObjId = firstTarget != null ? firstTarget.getObjectId() : 0;
final int unk = 0;
PacketSendUtility.broadcastPacketAndReceive(effector, new SM_CASTSPELL(effector.getObjectId(), skillTemplate
.getSkillId(), skillLevel, unk, targetObjId, skillTemplate.getDuration()));
}
/**
* Apply effects and perform actions specified in skill template
*/
private void endCast()
{
if(!effector.isCasting())
return;
//stop casting must be before preUsageCheck()
effector.setCasting(null);
if(!preUsageCheck())
return;
/**
* Create effects and precalculate result
*/
int spellStatus = 0;
List<Effect> effects = new ArrayList<Effect>();
if(skillTemplate.getEffects() != null)
{
int duration = skillTemplate.getEffects().getEffectsDuration();
for(Creature effected : effectedList)
{
int realDuration = duration;
if(effected instanceof Player && skillTemplate.getPvpDuration() != 0)
realDuration = duration * skillTemplate.getPvpDuration() / 100;
Effect effect = new Effect(effector, effected, skillTemplate, skillLevel, realDuration, itemTemplate);
effect.initialize();
spellStatus = effect.getSpellStatus().getId();
effects.add(effect);
}
}
/**
* If castspell - send SM_CASTSPELL_END packet
*/
if(skillTemplate.isActive() || skillTemplate.isToggle())
{
PacketSendUtility.broadcastPacketAndReceive(effector, new SM_CASTSPELL_END(effector, skillTemplate
.getSkillId(), skillLevel, firstTarget, effects, skillTemplate.getCooldown(), spellStatus));
}
/**
* Perform necessary actions (use mp,dp items etc)
*/
Actions skillActions = skillTemplate.getActions();
if(skillActions != null)
{
for(Action action : skillActions.getActions())
{
action.act(this);
}
}
/**
* Apply effects to effected objects
*/
for(Effect effect : effects)
{
effect.applyEffect();
}
/**
* Use penalty skill (now 100% success)
*/
startPenaltySkill();
}
/**
* Schedule actions/effects of skill (channeled skills)
*/
private void schedule(int delay)
{
ThreadPoolManager.getInstance().schedule(new Runnable()
{
public void run()
{
endCast();
}
}, delay);
}
/**
* Check all conditions before starting cast
*/
private boolean preCastCheck()
{
Conditions skillConditions = skillTemplate.getStartconditions();
return checkConditions(skillConditions);
}
/**
* Check all conditions before using skill
*/
private boolean preUsageCheck()
{
Conditions skillConditions = skillTemplate.getUseconditions();
return checkConditions(skillConditions);
}
private boolean checkConditions(Conditions conditions)
{
if(conditions != null)
{
for(Condition condition : conditions.getConditions())
{
if(!condition.verify(this))
{
return false;
}
}
}
return true;
}
private boolean setProperties(Properties properties)
{
if(properties != null)
{
for(Property property : properties.getProperties())
{
if(!property.set(this))
{
return false;
}
}
}
return true;
}
/**
* @return the effectedList
*/
public List<Creature> getEffectedList()
{
return effectedList;
}
/**
* @return the effector
*/
public Creature getEffector()
{
return effector;
}
/**
* @return the skillLevel
*/
public int getSkillLevel()
{
return skillLevel;
}
/**
* @return the skillStackLvl
*/
public int getSkillStackLvl()
{
return skillStackLvl;
}
/**
* @return the conditionChangeListener
*/
public StartMovingListener getConditionChangeListener()
{
return conditionChangeListener;
}
/**
* @return the skillTemplate
*/
public SkillTemplate getSkillTemplate()
{
return skillTemplate;
}
/**
* @return the firstTarget
*/
public Creature getFirstTarget()
{
return firstTarget;
}
/**
* @param firstTarget the firstTarget to set
*/
public void setFirstTarget(Creature firstTarget)
{
this.firstTarget = firstTarget;
}
/**
* @return true or false
*/
public boolean isPassive()
{
return skillTemplate.getActivationAttribute() == ActivationAttribute.PASSIVE;
}
/**
* @return the firstTargetRangeCheck
*/
public boolean isFirstTargetRangeCheck()
{
return firstTargetRangeCheck;
}
/**
* @param firstTargetRangeCheck the firstTargetRangeCheck to set
*/
public void setFirstTargetRangeCheck(boolean firstTargetRangeCheck)
{
this.firstTargetRangeCheck = firstTargetRangeCheck;
}
/**
* @param itemTemplate the itemTemplate to set
*/
public void setItemTemplate(ItemTemplate itemTemplate)
{
this.itemTemplate = itemTemplate;
}
}