public SM_CASTSPELL_RESULT(
     Skill skill,
     List<Effect> effects,
     int hitTime,
     boolean chainSuccess,
     int spellStatus,
     int dashStatus) {
   this.skill = skill;
   this.effector = skill.getEffector();
   this.target = skill.getFirstTarget();
   this.effects = effects;
   this.cooldown = effector.getSkillCooldown(skill.getSkillTemplate());
   this.spellStatus = spellStatus;
   this.chainSuccess = chainSuccess;
   this.targetType = 0;
   this.hitTime = hitTime;
   this.dashStatus = dashStatus;
 }
  /**
   * @param skill
   * @param properties
   * @return
   */
  public static final boolean set(Skill skill, Properties properties) {

    FirstTargetAttribute value = properties.getFirstTarget();
    skill.setFirstTargetAttribute(value);
    switch (value) {
      case ME:
        skill.setFirstTargetRangeCheck(false);
        skill.setFirstTarget(skill.getEffector());
        break;
      case TARGETORME:
        boolean changeTargetToMe = false;
        if (skill.getFirstTarget() == null) {
          skill.setFirstTarget(skill.getEffector());
        } else if (skill.getFirstTarget().isAttackableNpc()) {
          Player playerEffector = (Player) skill.getEffector();
          if (skill.getFirstTarget().isEnemy(playerEffector)) {
            changeTargetToMe = true;
          }
        } else if ((skill.getFirstTarget() instanceof Player)
            && (skill.getEffector() instanceof Player)) {
          Player playerEffected = (Player) skill.getFirstTarget();
          Player playerEffector = (Player) skill.getEffector();
          if (!playerEffected.getRace().equals(playerEffector.getRace())
              || playerEffected.isEnemy(playerEffector)) {
            changeTargetToMe = true;
          }
        } else if (skill.getFirstTarget() instanceof Npc) {
          Npc npcEffected = (Npc) skill.getFirstTarget();
          Player playerEffector = (Player) skill.getEffector();
          if (npcEffected.isEnemy(playerEffector)) {
            changeTargetToMe = true;
          }
        } else if ((skill.getFirstTarget() instanceof Summon)
            && (skill.getEffector() instanceof Player)) {
          Summon summon = (Summon) skill.getFirstTarget();
          Player playerEffected = summon.getMaster();
          Player playerEffector = (Player) skill.getEffector();
          if (playerEffected.isEnemy(playerEffector)) {
            changeTargetToMe = true;
          }
        }
        if (changeTargetToMe) {
          if (skill.getEffector() instanceof Player)
            PacketSendUtility.sendPacket(
                (Player) skill.getEffector(), SM_SYSTEM_MESSAGE.STR_SKILL_AUTO_CHANGE_TARGET_TO_MY);
          skill.setFirstTarget(skill.getEffector());
        }
        break;
      case TARGET:
        // Exception for effect skills which are not used directly
        if (skill.getSkillId() > 8000 && skill.getSkillId() < 9000) break;
        // Exception for NPC skills which applied on players
        if (skill.getSkillTemplate().getDispelCategory() == DispelCategoryType.NPC_BUFF
            || skill.getSkillTemplate().getDispelCategory()
                == DispelCategoryType.NPC_DEBUFF_PHYSICAL) break;

        if (skill.getFirstTarget() == null || skill.getFirstTarget().equals(skill.getEffector())) {
          if (skill.getEffector() instanceof Player) {
            if (skill.getSkillTemplate().getProperties().getTargetType()
                == TargetRangeAttribute.AREA) return skill.getFirstTarget() != null;

            TargetRelationAttribute relation =
                skill.getSkillTemplate().getProperties().getTargetRelation();
            TargetRangeAttribute type = skill.getSkillTemplate().getProperties().getTargetType();
            if ((relation != TargetRelationAttribute.ALL
                    && relation != TargetRelationAttribute.MYPARTY)
                || type == TargetRangeAttribute.PARTY
                || skill.getSkillId() == 3069) { // TODO: Remove ID, find logic!
              PacketSendUtility.sendPacket(
                  (Player) skill.getEffector(), SM_SYSTEM_MESSAGE.STR_SKILL_TARGET_IS_NOT_VALID);
              return false;
            }
          }
        }
        break;
      case MYPET:
        Creature effector = skill.getEffector();
        if (effector instanceof Player) {
          Summon summon = ((Player) effector).getSummon();
          if (summon != null) skill.setFirstTarget(summon);
          else return false;
        } else {
          return false;
        }
        break;
      case MYMASTER:
        Creature peteffector = skill.getEffector();
        if (peteffector instanceof Summon) {
          Player player = ((Summon) peteffector).getMaster();
          if (player != null) skill.setFirstTarget(player);
          else return false;
        } else {
          return false;
        }
        break;
      case PASSIVE:
        skill.setFirstTarget(skill.getEffector());
        break;
      case TARGET_MYPARTY_NONVISIBLE:
        Creature effected = skill.getFirstTarget();
        if (effected == null || skill.getEffector() == null) return false;
        if (!(effected instanceof Player)
            || !(skill.getEffector() instanceof Player)
            || !((Player) skill.getEffector()).isInGroup2()) return false;
        boolean myParty = false;
        for (Player member : ((Player) skill.getEffector()).getPlayerGroup2().getMembers()) {
          if (member == skill.getEffector()) continue;
          if (member == effected) {
            myParty = true;
            break;
          }
        }
        if (!myParty) return false;

        skill.setFirstTargetRangeCheck(false);
        break;
      case POINT:
        skill.setFirstTarget(skill.getEffector());
        skill.setFirstTargetRangeCheck(false);
        return true;
      default:
        break;
    }

    if (skill.getFirstTarget() != null) skill.getEffectedList().add(skill.getFirstTarget());
    return true;
  }
  @Override
  public boolean validate(Skill env) {
    if (env.getFirstTarget() == null || env.getEffector() == null) return false;

    return PositionUtil.isInFrontOfTarget(env.getEffector(), env.getFirstTarget());
  }
  @Override
  protected void writeImpl(AionConnection con) {
    writeD(effector.getObjectId());
    writeC(targetType);
    switch (targetType) {
      case 0:
      case 3:
      case 4:
        writeD(target.getObjectId());
        break;
      case 1:
        writeF(skill.getX());
        writeF(skill.getY());
        writeF(skill.getZ());
        break;
      case 2:
        writeF(skill.getX());
        writeF(skill.getY());
        writeF(skill.getZ());
        writeF(0); // unk1
        writeF(0); // unk2
        writeF(0); // unk3
        writeF(0); // unk4
        writeF(0); // unk5
        writeF(0); // unk6
        writeF(0); // unk7
        writeF(0); // unk8
        break;
    }
    writeH(skill.getSkillTemplate().getSkillId());
    writeC(skill.getSkillTemplate().getLvl());
    writeD(cooldown);
    writeH(hitTime);
    writeC(0); // unk

    /**
     * 0 : chain skill (counter too) 16 : no damage to all target like dodge, resist or effect size
     * is 0 32 : regular
     */
    if (effects.isEmpty()) writeH(16);
    else if (chainSuccess) writeH(32);
    else writeH(0);

    // Get dash status
    writeC(this.dashStatus);
    switch (this.dashStatus) {
      case 1:
      case 2:
      case 3:
      case 4:
      case 6:
        writeC(skill.getH());
        writeF(skill.getX());
        writeF(skill.getY());
        writeF(skill.getZ());
        break;
      default:
        break;
    }

    writeH(effects.size());
    for (Effect effect : effects) {
      Creature effected = effect.getEffected();

      if (effected != null) {
        writeD(effected.getObjectId());
        writeC(effect.getEffectResult().getId()); // 0 - NORMAL, 1 - ABSORBED, 2 - CONFLICT
        writeC(
            (int)
                (100f
                    * effected.getLifeStats().getCurrentHp()
                    / target.getLifeStats().getMaxHp())); // target %hp
      } else { // point point skills
        writeD(0);
        writeC(0);
        writeC(0);
      }

      writeC(
          (int)
              (100f
                  * effector.getLifeStats().getCurrentHp()
                  / effector.getLifeStats().getMaxHp())); // attacker %hp

      /**
       * Spell Status 1 : stumble 2 : knockback 4 : open aerial 8 : close aerial 16 : spin 32 :
       * block 64 : parry 128 :dodge 256 : resist
       */
      writeC(this.spellStatus);
      writeC(effect.getSkillMoveType().getId());
      writeH(0);
      writeC(effect.getCarvedSignet()); // current carve signet count

      switch (this.spellStatus) {
        case 1:
        case 2:
        case 4:
        case 8:
          writeF(effect.getTargetX());
          writeF(effect.getTargetY());
          writeF(effect.getTargetZ());
          break;
        case 16:
          writeC(effect.getEffector().getHeading());
          break;
        default:
          switch (effect.getSkillMoveType()) {
            case PULL:
            case KNOCKBACK:
              writeF(effect.getTargetX());
              writeF(effect.getTargetY());
              writeF(effect.getTargetZ());
            default:
              break;
          }
          break;
      }

      writeC(1); // loops size - always 1?
      // TODO for(lop size)
      {
        writeC(effect.isMphealInstant() ? 1 : 0);
        if (effect.isDelayedDamage()) writeD(0);
        else writeD(effect.getReserved1()); // TODO value from specific effect using loop
        writeC(effect.getAttackStatus().getId());

        // setting counter skill from packet to have the best synchronization of time with client
        if (effect.getEffected() instanceof Player) {
          if (effect.getAttackStatus().isCounterSkill())
            ((Player) effect.getEffected()).setLastCounterSkill(effect.getAttackStatus());
        }

        writeC(effect.getShieldDefense());

        /**
         * shield Type: 1: reflector 2: normal shield 8: protect effect (ex. skillId: 417 Bodyguard)
         * TODO find out 4
         */
        switch (effect.getShieldDefense()) {
          case 0:
          case 2:
            break;
          case 8:
          case 10:
            writeD(effect.getProtectorId()); // protectorId
            writeD(effect.getProtectedDamage()); // protected damage
            writeD(effect.getProtectedSkillId()); // skillId
            break;
          case 16:
            writeD(0);
            writeD(0);
            writeD(0);
            writeD(0);
            writeD(0);
            writeD(effect.getMpShield());
            writeD(effect.getReflectedSkillId());
            break;
          default:
            writeD(effect.getProtectorId()); // protectorId
            writeD(effect.getProtectedDamage()); // protected damage
            writeD(effect.getProtectedSkillId()); // skillId
            writeD(effect.getReflectedDamage()); // reflect damage
            writeD(effect.getReflectedSkillId()); // skill id
            writeD(0);
            writeD(0);
            break;
        }
      }
    }
  }