/*
* This file is part of the OdinMS Maple Story Server Copyright (C) 2008 ~ 2010
* Patrick Huy <patrick.huy@frz.cc> Matthias Butz <matze@odinms.de> Jan
* Christian Meyer <vimes@odinms.de>
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation. You may not use, modify or distribute this
* program under any other version of the GNU Affero General Public License.
*
* This program 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package javastory.channel.client;
import java.util.List;
import javastory.channel.ChannelCharacter;
import javastory.channel.server.StatEffect;
import javastory.game.AttackNature;
import javastory.game.Jobs;
import javastory.wz.WzData;
import javastory.wz.WzDataTool;
import com.google.common.collect.Lists;
public class Skill implements ISkill {
public static final int[] DUALBLADE_SKILLS = new int[] { 4311003, 4321000, 4331002, 4331005, 4341004, 4341007 };
//
public static final int[] EVAN_SKILLS_1 = new int[] { 22171000, 22171002, 22171003, 22171004 };
//
public static final int[] EVAN_SKILLS_2 = new int[] { 22181000, 22181001, 22181002, 22181003 };
//
private final int id;
private final List<StatEffect> effects = Lists.newArrayList();
private AttackNature element;
private byte level;
private int animationTime, requiredSkill, masterLevel;
private boolean action;
private boolean invisible;
private boolean chargeskill;
private Skill(final int id) {
super();
this.id = id;
}
@Override
public int getId() {
return this.id;
}
public static Skill loadFromData(final int id, final WzData data) {
final Skill ret = new Skill(id);
boolean isBuff = false;
final int skillType = WzDataTool.getInt("skillType", data, -1);
final String elem = WzDataTool.getString("elemAttr", data, null);
if (elem != null) {
ret.element = AttackNature.fromCharacter(elem.charAt(0));
} else {
ret.element = AttackNature.NEUTRAL;
}
ret.invisible = WzDataTool.getInt("invisible", data, 0) > 0;
ret.masterLevel = WzDataTool.getInt("masterLevel", data, 0);
// unfortunatly this is only set for a few skills so we have to do some more to figure out if it's a buff �.o
final WzData effect = data.getChildByPath("effect");
if (skillType != -1) {
if (skillType == 2) {
isBuff = true;
}
} else {
final WzData action_ = data.getChildByPath("action");
final WzData hit = data.getChildByPath("hit");
final WzData ball = data.getChildByPath("ball");
boolean action = false;
if (action_ == null) {
if (data.getChildByPath("prepare/action") != null) {
action = true;
} else {
switch (id) {
case 5201001:
case 5221009:
action = true;
break;
}
}
} else {
action = true;
}
ret.action = action;
isBuff = effect != null && hit == null && ball == null;
isBuff |= action_ != null && WzDataTool.getString("0", action_, "").equals("alert2");
switch (id) {
case 2301002: // heal is alert2 but not overtime...
case 2111003: // poison mist
case 12111005: // Flame Gear
case 2111002: // explosion
case 4211001: // chakra
case 2121001: // Big bang
case 2221001: // Big bang
case 2321001: // Big bang
isBuff = false;
break;
case 1004: // monster riding
case 10001004:
case 20001004:
case 20011004:
case 9101004: // hide is a buff -.- atleast for us o.o"
case 1111002: // combo
case 4211003: // pickpocket
case 4111001: // mesoup
case 15111002: // Super Transformation
case 5111005: // Transformation
case 5121003: // Super Transformation
case 13111005: // Alabtross
case 21000000: // Aran Combo
case 21101003: // Body Pressure
case 5211001: // Pirate octopus summon
case 5211002:
case 5220002: // wrath of the octopi
case 5211006: //homing beacon
case 5220011: //bullseye
case 22121001: //element reset
case 22131001: //magic shield -- NOT CODED
case 22141002: //magic booster
case 22151002: //killer wing
case 22151003: //magic resist -- NOT CODED
case 22171000: //maple warrior
case 22171004: //hero will
case 22181000: //onyx blessing
case 22181003: //soul stone -- NOT CODED
//case 22121000:
//case 22141003:
//case 22151001:
//case 22161002:
//tornado spin too ?
//case 4341006: //final cut
case 4331003: //owl spirit
case 4321000: //tornado spin
isBuff = true;
break;
}
}
ret.chargeskill = data.getChildByPath("keydown") != null;
for (final WzData level : data.getChildByPath("level")) {
ret.effects.add(StatEffect.loadSkillEffectFromData(level, id, isBuff));
}
final WzData reqDataRoot = data.getChildByPath("req");
if (reqDataRoot != null) {
for (final WzData reqData : reqDataRoot.getChildren()) {
ret.requiredSkill = Integer.parseInt(reqData.getName());
ret.level = (byte) WzDataTool.getInt(reqData, 1);
}
}
ret.animationTime = 0;
if (effect != null) {
for (final WzData effectEntry : effect) {
ret.animationTime += WzDataTool.getIntConvert("delay", effectEntry, 0);
}
}
return ret;
}
@Override
public StatEffect getEffect(final int level) {
return this.effects.get(level - 1);
}
@Override
public StatEffect getEffect(final ChannelCharacter chr, final int level) {
return this.effects.get(level - 1);
}
@Override
public boolean getAction() {
return this.action;
}
@Override
public boolean isChargeSkill() {
return this.chargeskill;
}
@Override
public boolean isInvisible() {
return this.invisible;
}
@Override
public boolean hasRequiredSkill() {
return this.level > 0;
}
@Override
public int getRequiredSkillLevel() {
return this.level;
}
@Override
public int getRequiredSkillId() {
return this.requiredSkill;
}
@Override
public byte getMaxLevel() {
return (byte) this.effects.size();
}
@Override
public boolean canBeLearnedBy(final int job) {
final int jid = job;
final int skillJob = this.id / 10000;
if (skillJob == 2001 && Jobs.isEvan(job)) {
return true; //special exception for evan -.-
}
if (job < 1000) {
if (jid / 100 != skillJob / 100 && skillJob / 100 != 0) { // wrong job
return false;
}
} else {
if (jid / 1000 != skillJob / 1000 && skillJob / 1000 != 0) { // wrong job
return false;
}
}
if (Jobs.isAdventurer(skillJob) && !Jobs.isAdventurer(job)) {
return false;
} else if (Jobs.isCygnus(skillJob) && !Jobs.isCygnus(job)) {
return false;
} else if (Jobs.isAran(skillJob) && !Jobs.isAran(job)) {
return false;
} else if (Jobs.isEvan(skillJob) && !Jobs.isEvan(job)) {
return false;
}
if (skillJob / 10 % 10 > jid / 10 % 10) { // wrong 2nd job
return false;
}
if (skillJob % 10 > jid % 10) { // wrong 3rd/4th job
return false;
}
return true;
}
@Override
public boolean isFourthJob() {
final int jobFamily = this.id / 10000;
if (jobFamily >= 2212 && jobFamily < 3000) {
//evan skill
return jobFamily % 10 >= 7;
}
if (jobFamily >= 430 && jobFamily <= 434) {
//db skill
return jobFamily % 10 == 4 || isMasterSkill(this.id);
}
return jobFamily % 10 == 2;
}
@Override
public AttackNature getElement() {
return this.element;
}
@Override
public int getAnimationTime() {
return this.animationTime;
}
@Override
public int getMasterLevel() {
return this.masterLevel;
}
public static boolean isMasterSkill(final int skill) {
for (final int i : DUALBLADE_SKILLS) {
if (i == skill) {
return true;
}
}
return false;
}
@Override
public boolean isBeginnerSkill() {
final String idString = String.valueOf(this.id);
if (idString.length() == 4 || idString.length() == 1) {
return true;
}
return false;
}
}