Skip to content

Commit

Permalink
Fixed skill chains got interrupted by delayed skills (introduced by d…
Browse files Browse the repository at this point in the history
…538cd2)

This also affected charge skills. This new solution no longer requires to cast a separate skill, as delayed skill effects should be applied directly.
  • Loading branch information
neon-dev committed May 21, 2024
1 parent cf2987b commit 882014d
Show file tree
Hide file tree
Showing 10 changed files with 164 additions and 196 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.aionemu.gameserver.skillengine;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.LoggerFactory;

import com.aionemu.gameserver.dataholders.DataManager;
Expand All @@ -11,6 +14,7 @@
import com.aionemu.gameserver.skillengine.effect.EffectType;
import com.aionemu.gameserver.skillengine.model.*;
import com.aionemu.gameserver.skillengine.model.Effect.ForceType;
import com.aionemu.gameserver.skillengine.properties.Properties;

/**
* @author ATracer
Expand Down Expand Up @@ -152,6 +156,23 @@ public Effect applyEffectDirectly(SkillTemplate skillTemplate, int skillLevel, C
return applyEffect(effector, effected, skillTemplate, skillLevel, null, ForceType.DEFAULT);
}

/**
* Applies the skill's effects to one or multiple targets, depending on its template properties.
*
* @return affected targets
*/
public List<Creature> applyEffectsDirectly(int skillId, Creature effector, Creature firstTarget, float x, float y, float z) {
SkillTemplate skillTemplate = DataManager.SKILL_DATA.getSkillTemplate(skillId);
Properties properties = skillTemplate.getProperties();
List<Creature> targets = new ArrayList<>();
targets.add(firstTarget);
if (properties != null) // add valid targets in range
properties.validateEffectedList(targets, firstTarget, effector, skillTemplate, x, y, z);
for (Creature target : targets)
applyEffect(effector, target, skillTemplate, skillTemplate.getLvl(), null, ForceType.DEFAULT);
return targets;
}

/**
* Similar function to {@link #applyEffectDirectly}, but effect is not forced. That means it checks for resists etc.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

import javax.xml.bind.annotation.XmlAttribute;

import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.skillengine.SkillEngine;
import com.aionemu.gameserver.skillengine.model.Effect;
import com.aionemu.gameserver.skillengine.model.SkillTemplate;

/**
* @author kecimis, Cheatkiller
Expand All @@ -23,9 +21,7 @@ public void applyEffect(final Effect effect) {
@Override
public void endEffect(Effect effect) {
super.endEffect(effect);
if (effect.isEndedByTime() && !effect.getEffected().isDead()) {
SkillTemplate st = DataManager.SKILL_DATA.getSkillTemplate(skillId);
SkillEngine.getInstance().getSkill(effect.getEffector(), skillId, st.getLvl(), effect.getEffected()).useWithoutPropSkill();
}
if (effect.isEndedByTime())
SkillEngine.getInstance().applyEffectsDirectly(skillId, effect.getEffector(), effect.getEffected(), effect.getTargetX(), effect.getTargetY(), effect.getTargetZ());
}
}
56 changes: 13 additions & 43 deletions game-server/src/com/aionemu/gameserver/skillengine/model/Skill.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import com.aionemu.gameserver.ai.handler.ShoutEventHandler;
import com.aionemu.gameserver.ai.manager.SkillAttackManager;
import com.aionemu.gameserver.configs.main.CustomConfig;
import com.aionemu.gameserver.configs.main.GeoDataConfig;
import com.aionemu.gameserver.configs.main.SecurityConfig;
import com.aionemu.gameserver.controllers.attack.AttackStatus;
import com.aionemu.gameserver.controllers.observer.ActionObserver;
Expand All @@ -25,7 +24,6 @@
import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.Npc;
import com.aionemu.gameserver.model.gameobjects.VisibleObject;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.skill.NpcSkillEntry;
import com.aionemu.gameserver.model.stats.container.StatEnum;
Expand All @@ -50,7 +48,6 @@
import com.aionemu.gameserver.utils.PositionUtil;
import com.aionemu.gameserver.utils.ThreadPoolManager;
import com.aionemu.gameserver.utils.audit.AuditLogger;
import com.aionemu.gameserver.world.geo.GeoService;

/**
* @author ATracer, Wakizashi, Neon
Expand Down Expand Up @@ -78,8 +75,6 @@ public class Skill {
private float z;
private byte h;
private int boostSkillCost;
private FirstTargetAttribute firstTargetAttribute;
private TargetRangeAttribute targetRangeAttribute;
/**
* Duration that depends on BOOST_CASTING_TIME
*/
Expand Down Expand Up @@ -189,7 +184,7 @@ private boolean validateEffectedList() {
}

if (targetType == 0 && effectedList.isEmpty()) { // target selected but no target will be hit
if (targetRangeAttribute != TargetRangeAttribute.AREA) { // don't restrict AoE activation
if (getTargetRangeAttribute() != TargetRangeAttribute.AREA) { // don't restrict AoE activation
if (effector instanceof Player player)
PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_SKILL_TARGET_IS_NOT_VALID());
return false;
Expand Down Expand Up @@ -880,48 +875,48 @@ public boolean isFirstTargetRangeCheck() {
return firstTargetRangeCheck;
}

/**
* @param firstTargetAttribute
* the firstTargetAttribute to set
*/
public void setFirstTargetAttribute(FirstTargetAttribute firstTargetAttribute) {
this.firstTargetAttribute = firstTargetAttribute;
public FirstTargetAttribute getFirstTargetAttribute() {
return skillTemplate.getProperties() == null ? null : skillTemplate.getProperties().getFirstTarget();
}

public TargetRangeAttribute getTargetRangeAttribute() {
return skillTemplate.getProperties() == null ? null : skillTemplate.getProperties().getTargetType();
}

/**
* @return true if the present skill is a non-targeted, non-point AOE skill
*/
public boolean isNonTargetAOE() {
return (firstTargetAttribute == FirstTargetAttribute.ME && targetRangeAttribute == TargetRangeAttribute.AREA);
return getFirstTargetAttribute() == FirstTargetAttribute.ME && getTargetRangeAttribute() == TargetRangeAttribute.AREA;
}

/**
* @return true if the present skill is a targeted AOE skill
*/
private boolean isTargetAOE() {
return (firstTargetAttribute == FirstTargetAttribute.TARGET && targetRangeAttribute == TargetRangeAttribute.AREA);
return getFirstTargetAttribute() == FirstTargetAttribute.TARGET && getTargetRangeAttribute() == TargetRangeAttribute.AREA;
}

/**
* @return true if the present skill is a self buff includes items (such as scroll buffs)
*/
public boolean isSelfBuff() {
return (firstTargetAttribute == FirstTargetAttribute.ME && targetRangeAttribute == TargetRangeAttribute.ONLYONE
&& skillTemplate.getSubType() == SkillSubType.BUFF && !skillTemplate.isDeityAvatar());
return getFirstTargetAttribute() == FirstTargetAttribute.ME && getTargetRangeAttribute() == TargetRangeAttribute.ONLYONE
&& skillTemplate.getSubType() == SkillSubType.BUFF && !skillTemplate.isDeityAvatar();
}

/**
* @return true if the present skill has self as first target
*/
public boolean isFirstTargetSelf() {
return (firstTargetAttribute == FirstTargetAttribute.ME);
return getFirstTargetAttribute() == FirstTargetAttribute.ME;
}

/**
* @return true if the present skill is a Point skill
*/
public boolean isPointSkill() {
return (firstTargetAttribute == FirstTargetAttribute.POINT);
return getFirstTargetAttribute() == FirstTargetAttribute.POINT;
}

/**
Expand All @@ -944,14 +939,6 @@ public int getItemObjectId() {
return itemObjectId;
}

/**
* @param targetRangeAttribute
* the targetRangeAttribute to set
*/
public void setTargetRangeAttribute(TargetRangeAttribute targetRangeAttribute) {
this.targetRangeAttribute = targetRangeAttribute;
}

public void setTargetType(int targetType, float x, float y, float z) {
this.targetType = targetType;
this.x = x;
Expand Down Expand Up @@ -1074,23 +1061,6 @@ private boolean isCastTimeFixed() {
return false;
}

private boolean isGroundSkill() {
return skillTemplate.isGroundSkill();
}

public boolean shouldAffectTarget(VisibleObject object) {
// If creature is at least 2 meters above the terrain, ground skill cannot be applied
if (GeoDataConfig.GEO_ENABLE) {
if (isGroundSkill()) {
float geoZ = GeoService.getInstance().getZ(object, object.getZ() + 2, object.getZ() - 100);
if (!Float.isNaN(geoZ) && object.getZ() - geoZ > 2f)
return false;
}
return GeoService.getInstance().canSee(getFirstTarget(), object);
}
return true;
}

public void setChainCategory(String chainCategory) {
this.chainCategory = chainCategory;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ public class FirstTargetProperty {

public static boolean set(Skill skill, Properties properties) {
Creature effector = skill.getEffector();
FirstTargetAttribute value = properties.getFirstTarget();
skill.setFirstTargetAttribute(value);
switch (value) {
switch (properties.getFirstTarget()) {
case ME:
skill.setFirstTargetRangeCheck(false);
skill.setFirstTarget(effector);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,42 @@

import com.aionemu.gameserver.model.gameobjects.Creature;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.skillengine.model.Skill;
import com.aionemu.gameserver.utils.PositionUtil;

/**
* @author MrPoke, Neon
*/
public class MaxCountProperty {

public static boolean set(Skill skill, Properties properties) {
public static boolean set(Properties properties, Properties.ValidationResult result) {
TargetRangeAttribute value = properties.getTargetType();
int maxCount = properties.getTargetMaxCount();
if (properties.getFirstTarget() == FirstTargetAttribute.TARGET && value == TargetRangeAttribute.AREA) // firstTarget doesn't count on AREA skills (see skill 1245 or 16689)
maxCount += 1;
if (maxCount == 0 || skill.getEffectedList().size() <= maxCount)
if (maxCount == 0 || result.getTargets().size() <= maxCount)
return true;

switch (value) {
case AREA:
case PARTY:
case PARTY_WITHPET:
Creature firstTarget = skill.getFirstTarget();
if (firstTarget == null)
if (result.getFirstTarget() == null)
return false;

Set<Creature> nearestCreatures = skill.getEffectedList().stream()
.sorted(Comparator.comparingDouble(c -> PositionUtil.getDistance(firstTarget, c)))
Set<Creature> nearestCreatures = result.getTargets().stream()
.sorted(Comparator.comparingDouble(c -> PositionUtil.getDistance(result.getFirstTarget(), c)))
.limit(maxCount)
.collect(Collectors.toSet());

// rebuild effected list with correct number of creatures and their summons
if (value == TargetRangeAttribute.PARTY_WITHPET) {
for (Object creature : nearestCreatures.toArray()) {
Creature summon = creature instanceof Player ? ((Player) creature).getSummon() : null;
if (summon != null && skill.getEffectedList().contains(summon))
if (summon != null && result.getTargets().contains(summon))
nearestCreatures.add(summon);
}
}
skill.getEffectedList().retainAll(nearestCreatures);
result.getTargets().retainAll(nearestCreatures);
}
return true;
}
Expand Down
Loading

0 comments on commit 882014d

Please sign in to comment.