package handling.channel.handler;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.PlayerStats;
import client.Skill;
import client.SkillFactory;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import client.status.MonsterStatus;
import client.status.MonsterStatusEffect;
import constants.GameConstants;
import constants.SkillConstants;
import handling.world.WorldBroadcastService;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import server.AutobanManager;
import server.MapleStatEffect;
import server.Randomizer;
import server.life.Element;
import server.life.ElementalEffectiveness;
import server.life.MapleMonster;
import server.life.MapleMonsterStats;
import server.maps.MapleMap;
import server.maps.MapleMapItem;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import server.skill.冒险家.侠客;
import server.skill.冒险家.刺客;
import server.skill.冒险家.牧师;
import server.skill.冒险家.独行客;
import tools.AttackPair;
import tools.FileoutputUtil;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.packet.BuffPacket;
import tools.packet.SkillPacket;
public class DamageParse {
private static final Logger log = Logger.getLogger(DamageParse.class);
public static void applyAttack(AttackInfo attack, Skill theSkill, MapleCharacter player, int attackCount, double maxDamagePerMonster, MapleStatEffect effect, AttackType attack_type, int visProjectile) {
MapleMonster monster;
if (!player.isAlive()) {
player.dropMessage(5, "你升天了还想打我?");
return;
}
if (attack.skillId != 0) {
if (player.isShowPacket()) {
player.dropMessage(5, "[技能攻击] 使用技能[" + attack.skillId + "]进行攻击");
}
if (effect == null) {
player.getClient().getSession().write(MaplePacketCreator.enableActions());
return;
}
if (GameConstants.isMulungSkill(attack.skillId)) {
if (player.getMapId() / 10000 != 92502) {
return;
}
if (player.getMulungEnergy() < 10000) {
return;
}
player.mulung_EnergyModify(false);
} else if (GameConstants.isPyramidSkill(attack.skillId)) {
if (player.getMapId() / 1000000 != 926) {
return;
}
} else if (GameConstants.isInflationSkill(attack.skillId)) {
if (player.getBuffedValue(MapleBuffStat.GIANT_POTION) == null) {
return;
}
} else if ((attack.numAttacked > effect.getMobCount(player)) && (attack.skillId != 1220010) && (attack.skillId != 32121003)) {
if (player.isShowPacket()) {
player.dropMessage(0, "物理怪物数量检测 => 封包解析次数: " + attack.numAttacked + " 服务端设置次数: " + effect.getMobCount(player));
}
return;
}
}
boolean useAttackCount = !GameConstants.is不检测次数(attack.skillId);
int totDamage = 0;
MapleMap map = player.getMap();
// Point Original_Pos = player.getPosition();
// else if (map.isPartyPvpMap()) {
// MaplePvp.doPartyPvP(player, map, attack, effect);
// } else if (map.isGuildPvpMap()) {
// MaplePvp.doGuildPvP(player, map, attack, effect);
// }
if (attack.skillId == 独行客.金钱炸弹) {
for (AttackPair oned : attack.allDamage) {
if (oned.attack != null) {
continue;
}
MapleMapObject mapobject = map.getMapObject(oned.objectid, MapleMapObjectType.ITEM);
if (mapobject != null) {
MapleMapItem mapitem = (MapleMapItem) mapobject;
mapitem.getLock().lock();
try {
if (mapitem.getMeso() > 0) {
if (mapitem.isPickedUp()) {
return;
}
map.removeMapObject(mapitem);
//map.broadcastMessage(InventoryPacket.explodeDrop(mapitem.getObjectId()));
mapitem.setPickedUp(true);
} else {
return;
}
} finally {
mapitem.getLock().unlock();
}
} else {
return;
}
}
}
int totDamageToOneMonster = 0;
long hpMob = 0L;
PlayerStats stats = player.getStat();
int criticalDamage = stats.passive_sharpeye_percent();
int shdowPartnerAttackPercentage = 0;
if ((attack_type == AttackType.RANGED_WITH_SHADOWPARTNER) || (attack_type == AttackType.NON_RANGED_WITH_MIRROR)) {
MapleStatEffect shadowPartnerEffect = player.getStatForBuff(MapleBuffStat.影分身);
if (shadowPartnerEffect != null) {
shdowPartnerAttackPercentage += shadowPartnerEffect.getShadowDamage();
}
attackCount /= 2;
}
shdowPartnerAttackPercentage *= (criticalDamage + 100) / 100;
if ((attack.skillId == 4221014) || (attack.skillId == 4221016)) {
shdowPartnerAttackPercentage *= 30;
}
if (attack.skillId == 3120017) { //三彩箭矢
effect.getMonsterStati().clear();
if (player.getmod() == 3) {
effect.getMonsterStati().put(MonsterStatus.中毒, 1);
}
}
int maxDamagePerHit = 0;
int maxMaxDamageOver = 0;
for (AttackPair oned : attack.allDamage) {
monster = map.getMonsterByOid(oned.objectid);
if (monster != null && monster.getLinkCID() <= 0) {
totDamageToOneMonster = 0;
hpMob = monster.getMobMaxHp();
MapleMonsterStats monsterstats = monster.getStats();
int fixeddmg = monsterstats.getFixedDamage();
boolean Tempest = (monster.getStatusSourceID(MonsterStatus.结冰) == 21120006) || (attack.skillId == 21120006) || (attack.skillId == 1221011);
if (!Tempest) {
maxDamagePerHit = CalculateMaxWeaponDamagePerHit(player, monster, attack, theSkill, effect, maxDamagePerMonster, criticalDamage);
}
byte overallAttackCount = 0;
for (Pair eachde : oned.attack) {
Integer eachd = (Integer) eachde.left;
overallAttackCount = (byte) (overallAttackCount + 1);
if ((useAttackCount) && (overallAttackCount - 1 == attackCount)) {
maxDamagePerHit = (int) (maxDamagePerHit / 100 * (shdowPartnerAttackPercentage * (monsterstats.isBoss() ? stats.getBossDamageRate(attack.skillId) : stats.getDamageRate()) / 100.0D));
}
if ((player.isShowPacket()) && (eachd > 0)) {
player.dropMessage(0, new StringBuilder().append("物理攻击打怪伤害 : ").append(eachd).append(" 服务端预计伤害 : ").append(maxDamagePerHit).append(" 是否超过 : ").append(eachd > maxDamagePerHit).toString());
}
if (fixeddmg != -1) {
if (monsterstats.getOnlyNoramlAttack()) {
eachd = attack.skillId != 0 ? 0 : fixeddmg;
} else {
eachd = fixeddmg;
}
} else if (monsterstats.getOnlyNoramlAttack()) {
eachd = attack.skillId != 0 ? 0 : Math.min(eachd, maxDamagePerHit);
} else if (!player.isGM()) {
if (Tempest) {
if (eachd > monster.getMobMaxHp()) {
eachd = (int) Math.min(monster.getMobMaxHp(), 2147483647L);
}
} else if (((player.getJob() >= 3200) && (player.getJob() <= 3212) ) || (attack.skillId == 23121003) || (((player.getJob() < 3200) || (player.getJob() > 3212)) )) {
if ((eachd > maxDamagePerHit) && (maxDamagePerHit > 2)) {
if ((eachd > maxDamagePerHit * 5) && (attack.skillId != 4001344)) {//TODO 开启攻击异常封号
String banReason = player.getName() + " 被系统封号.[异常攻击伤害值: " + eachd + ", 预计伤害: " + maxDamagePerHit + ", 怪物ID: " + monster.getId() + "] [职业: " + player.getJob() + ", 等级: " + player.getLevel() + ", 技能: " + attack.skillId + "]";
if (eachd > maxDamagePerHit * 7) {
FileoutputUtil.log(FileoutputUtil.攻击异常, "玩家[" + player.getName() + " 职业: " + player.getJobName() + "] 使用技能: " + theSkill.getName() + "超过系统计算攻击:" + maxDamagePerHit * 7 + " 玩家攻击:" + eachd);
player.getClient().getSession().write(MaplePacketCreator.enableActions());
//AutobanManager.getInstance().autoban(player.getClient(), banReason);
return;
}
}
}
} else if (eachd > maxDamagePerHit) {
eachd = maxDamagePerHit;
}
}
totDamageToOneMonster += eachd;
}
totDamage += totDamageToOneMonster;
player.checkMonsterAggro(monster);
if (player.getBuffedValue(MapleBuffStat.敛财术) != null) {
handlePickPocket(player, monster, oned);
}
if (totDamageToOneMonster > 0) {
if (attack.skillId == 刺客.生命吸收) {
int rev = Math.round((effect.getX() * totDamage)/100);
player.addHP(rev);
}
monster.damage(player, totDamageToOneMonster, true, attack.skillId);
//TODO 添加被动攻击技能处理 进阶攻击之类 召唤Mist之类的
player.handle被动触发技能(monster, attack.skillId);
//TODO 攻击前技能处理
player.onAttack(monster.getMobMaxHp(), monster.getMobMaxMp(), attack.skillId, monster.getObjectId(), totDamage);
//处理被动技能效果
switch (attack.skillId) {
case 4001334:
case 4001344:
case 4101008:
case 4101010:
case 4111010:
case 4111015:
case 4121013:
case 4201012:
case 4211002:
case 4211011:
case 4221007:
case 4221010:
case 4221014:
case 4311002:
case 4311003:
case 4321004:
case 4321006:
case 4331000:
case 4331006:
case 4341002:
case 4341004:
case 4341009:
int[] skills = {4110011, 4210010, 4320005, 14110004};
Skill skill = null;
MapleStatEffect venomEffect = null;
for (int i : skills) {
skill = SkillFactory.getSkill(i);
if (player.getTotalSkillLevel(skill) > 0) {
venomEffect = skill.getEffect(player.getTotalSkillLevel(skill));
if (venomEffect.makeChanceResult()) {
monster.applyStatus(player, new MonsterStatusEffect(MonsterStatus.中毒, 1, i, null, false), true, venomEffect.getDuration(), true, venomEffect);
}
break;
}
}
break;
case 侠客.神通术:
monster.handleSteal(player);
break;
}
if (totDamageToOneMonster > 0) {
Item weapon_ = player.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11);
if (weapon_ != null) {
MonsterStatus stat = GameConstants.getStatFromWeapon(weapon_.getItemId());
if ((stat != null) && (Randomizer.nextInt(100) < GameConstants.getStatChance())) {
MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(stat, Integer.valueOf(GameConstants.getXForStat(stat)), GameConstants.getSkillForStat(stat), null, false);
monster.applyStatus(player, monsterStatusEffect, false, 10000L, false, null);
}
}
if ((player.getJob() == 121) || (player.getJob() == 122)) {
Skill skill = SkillFactory.getSkill(1201012);
if (player.isBuffFrom(MapleBuffStat.属性攻击, skill)) {
MapleStatEffect eff = skill.getEffect(player.getTotalSkillLevel(skill));
MonsterStatusEffect monsterStatusEffect = new MonsterStatusEffect(MonsterStatus.结冰, Integer.valueOf(1), skill.getId(), null, false);
monster.applyStatus(player, monsterStatusEffect, false, eff.getY() * 2000, true, eff);
}
}
}
if (effect != null) {
if (effect.getMonsterStati().size() > 0 && effect.makeChanceResult()) {
for (Map.Entry z : effect.getMonsterStati().entrySet()) {
monster.applyStatus(player, new MonsterStatusEffect((MonsterStatus) z.getKey(), (Integer) z.getValue(), theSkill.getId(), null, false), effect.isPoison(), effect.getDuration(), true, effect);
}
}
}
}
}
}
if (totDamageToOneMonster > 0) {
//TODO 攻击后技能处理
player.afterAttack(attack.numAttacked, attack.numDamage, attack.skillId);
}
if (effect != null && ((attack.skillId != 0) && ((attack.numAttacked > 0) || (attack.skillId != 4341002)) && (!GameConstants.isNoDelaySkill(attack.skillId)))) {
if (NotEffectforAttack(attack.skillId)) {//TODO BUFF类技能攻击不应该再给与BUFF状态
effect.applyTo(player, attack.position);
}
}
}
public static boolean NotEffectforAttack(int skill) {//TODO 添加有BUFF状态的技能攻击不应该再给与BUFF状态
switch (skill) {
case 31121005:
case 61120007:
case 61101002:
case 13121054:
case 4341052:
case 3120019:
case 2121054:
case 24121005:
case 32121003:
case 65121003:
case 14121004:
case 35121052:
case 33121012:
return false;
default:
return true;
}
}
public static void applyAttackMagic(AttackInfo attack, Skill theSkill, MapleCharacter player, MapleStatEffect effect, double maxDamagePerMonster) {
MapleMonster monster;
if (!player.isAlive()) {
return;
}
if ((attack.real) && (GameConstants.getAttackDelay(attack.skillId, theSkill) >= 50)) {
//player.getCheatTracker().checkAttack(attack.skillId, attack.lastAttackTickCount);
}
int mobCount = effect.getMobCount(player);
int attackCount = effect.getAttackCount(player);
if (GameConstants.isMulungSkill(attack.skillId)) {
if (player.getMapId() / 10000 != 92502) {
return;
}
if (player.getMulungEnergy() < 10000) {
return;
}
player.mulung_EnergyModify(false);
} else if (GameConstants.isPyramidSkill(attack.skillId)) {
if (player.getMapId() / 1000000 != 926) {
return;
}
} else if ((GameConstants.isInflationSkill(attack.skillId))
&& (player.getBuffedValue(MapleBuffStat.GIANT_POTION) == null)) {
return;
}
PlayerStats stats = player.getStat();
Element element = theSkill.getElement();
int maxDamagePerHit = 0;
int totDamage = 0;
int CriticalDamage = stats.passive_sharpeye_percent();
Skill eaterSkill = SkillFactory.getSkill(GameConstants.getMPEaterForJob(player.getJob()));
int eaterLevel = player.getTotalSkillLevel(eaterSkill);
MapleMap map = player.getMap();
for (AttackPair oned : attack.allDamage) {
monster = map.getMonsterByOid(oned.objectid);
if ((monster != null) && (monster.getLinkCID() <= 0)) {
boolean Tempest = (monster.getStatusSourceID(MonsterStatus.结冰) == 21120006) && (!monster.getStats().isBoss());
int totDamageToOneMonster = 0;
MapleMonsterStats monsterstats = monster.getStats();
int fixeddmg = monsterstats.getFixedDamage();
if (!Tempest) {
maxDamagePerHit = CalculateMaxMagicDamagePerHit(player, theSkill, monster, monsterstats, stats, element, CriticalDamage, maxDamagePerMonster, effect);
}
byte overallAttackCount = 0;
for (Pair eachde : oned.attack) {
Integer eachd = (Integer) eachde.left;
overallAttackCount = (byte) (overallAttackCount + 1);
if (fixeddmg != -1) {
eachd = monsterstats.getOnlyNoramlAttack() ? 0 : fixeddmg;
} else {
if (player.isShowPacket()) {
player.dropMessage(0, new StringBuilder().append("魔法攻击打怪伤害 : ").append(eachd).append(" 服务端预计伤害 : ").append(maxDamagePerHit).append(" 是否超过 : ").append(eachd > maxDamagePerHit).toString());
}
if (monsterstats.getOnlyNoramlAttack()) {
eachd = 0;
} else if (!player.isGM()) {
if (Tempest) {
if (eachd > monster.getMobMaxHp()) {
eachd = (int) Math.min(monster.getMobMaxHp(), 2147483647L);
}
} else if (eachd > maxDamagePerHit) {
eachd = maxDamagePerHit;
}
}
}
totDamageToOneMonster += eachd;
}
totDamage += totDamageToOneMonster;
player.checkMonsterAggro(monster);
if ((GameConstants.getAttackDelay(attack.skillId, theSkill) >= 50) && (!GameConstants.isNoDelaySkill(attack.skillId)) && (!GameConstants.is不检测范围(attack.skillId)) && (!monster.getStats().isBoss()) && (player.getTruePosition().distanceSq(monster.getTruePosition()) > GameConstants.getAttackRange(effect, player.getStat().defRange) * 3)
&& (player.getMapId() != 703002000)) {
WorldBroadcastService.getInstance().broadcastGMMessage(MaplePacketCreator.serverMessageRedText("[GM 信息] " + player.getName() + " ID: " + player.getId() + " (等级 " + player.getLevel() + ") 攻击范围异常。职业: " + player.getJob() + " 技能: " + attack.skillId + " [范围: " + player.getTruePosition().distanceSq(monster.getTruePosition()) + " 预期: " + GameConstants.getAttackRange(effect, player.getStat().defRange) * 3 + "]"));
}
if ((attack.skillId == 牧师.群体治愈) && (!monsterstats.getUndead())) {
return;
}
if (totDamageToOneMonster > 0) {
monster.damage(player, totDamageToOneMonster, true, attack.skillId);
// if (monster.isBuffed(MonsterStatus.反射魔攻)) {
// player.addHP(-(7000 + Randomizer.nextInt(8000)));
// }
// if (player.getBuffedValue(MapleBuffStat.缓速术) != null) {
// MapleStatEffect eff = player.getStatForBuff(MapleBuffStat.缓速术);
// if ((eff != null) && (eff.makeChanceResult()) && (!monster.isBuffed(MonsterStatus.速度))) {
// monster.applyStatus(player, new MonsterStatusEffect(MonsterStatus.速度, eff.getX(), eff.getSourceId(), null, false), false, eff.getY() * 1000, true, eff);
// }
// }
player.handle被动触发技能(monster, attack.skillId);
player.onAttack(monster.getMobMaxHp(), monster.getMobMaxMp(), attack.skillId, monster.getObjectId(), totDamage);
switch (attack.skillId) {
case 2211010:
monster.setTempEffectiveness(Element.ICE, effect.getDuration());
break;
case 2121003:
monster.setTempEffectiveness(Element.FIRE, effect.getDuration());
}
if ((effect.getMonsterStati().size() > 0)
&& (effect.makeChanceResult())) {
for (Map.Entry z : effect.getMonsterStati().entrySet()) {
monster.applyStatus(player, new MonsterStatusEffect((MonsterStatus) z.getKey(), (Integer) z.getValue(), theSkill.getId(), null, false), effect.isPoison(), effect.getDuration(), true, effect);
}
}
if (eaterLevel > 0) {
eaterSkill.getEffect(eaterLevel).applyPassive(player, monster);
}
} else if ((attack.skillId == 27101101) && (effect.getMonsterStati().size() > 0)
&& (effect.makeChanceResult())) {
for (Map.Entry z : effect.getMonsterStati().entrySet()) {
monster.applyStatus(player, new MonsterStatusEffect((MonsterStatus) z.getKey(), (Integer) z.getValue(), theSkill.getId(), null, false), effect.isPoison(), effect.getDuration(), true, effect);
}
}
}
}
if (attack.skillId != 牧师.群体治愈) {
effect.applyTo(player);
}
}
private static int CalculateMaxMagicDamagePerHit(MapleCharacter chr, Skill skill, MapleMonster monster, MapleMonsterStats mobstats, PlayerStats stats, Element elem, Integer sharpEye, double maxDamagePerMonster, MapleStatEffect attackEffect) {
int dLevel = Math.max(mobstats.getLevel() - chr.getLevel(), 0) * 2;
int HitRate = Math.min((int) Math.floor(Math.sqrt(stats.getAccuracy())) - (int) Math.floor(Math.sqrt(mobstats.getEva())) + 100, 100);
if (dLevel > HitRate) {
HitRate = dLevel;
}
HitRate -= dLevel;
if ((HitRate <= 0) && ((!GameConstants.is新手职业(skill.getId() / 10000)) || (skill.getId() % 10000 != 1000))) {
return 0;
}
int CritPercent = sharpEye;
ElementalEffectiveness ee = monster.getEffectiveness(elem);
double elemMaxDamagePerMob;
switch (ee) {
case IMMUNE:
elemMaxDamagePerMob = 1.0D;
break;
default:
elemMaxDamagePerMob = ElementalStaffAttackBonus(elem, maxDamagePerMonster * ee.getValue(), stats);
}
int MDRate = monster.getStats().getMDRate();
MonsterStatusEffect pdr = monster.getBuff(MonsterStatus.魔防);
if (pdr != null) {
MDRate += pdr.getX();
}
elemMaxDamagePerMob -= elemMaxDamagePerMob * (Math.max(MDRate - stats.getIgnoreMobpdpR(skill.getId()) - attackEffect.getIgnoreMob(), 0) / 100.0D);
elemMaxDamagePerMob += elemMaxDamagePerMob / 100.0D * CritPercent;
elemMaxDamagePerMob *= ((monster.getStats().isBoss() ? chr.getStat().getBossDamageRate(skill.getId()) : chr.getStat().getDamageRate()) + attackEffect.getDamage()) / 100.0D;
elemMaxDamagePerMob += elemMaxDamagePerMob * (chr.getDamageIncrease(monster.getObjectId()) / 100.0D);
if (GameConstants.is新手职业(skill.getId() / 10000)) {
switch (skill.getId() % 10000) {
case 1000:
elemMaxDamagePerMob = 40.0D;
break;
case 1020:
elemMaxDamagePerMob = 1.0D;
break;
case 1009:
elemMaxDamagePerMob = monster.getStats().isBoss() ? monster.getMobMaxHp() / 30L * 100L : monster.getMobMaxHp();
}
}
int maxDamagePerMob = (int) elemMaxDamagePerMob;
switch (skill.getId()) {
case 32001000:
case 32101000:
case 32111002:
case 32121002:
maxDamagePerMob = (int) (maxDamagePerMob * 1.5D);
break;
case 27121303:
maxDamagePerMob = chr.getMaxDamageOver(skill.getId());
}
if ((monster.getId() >= 9400900) && (monster.getId() <= 9400911)) {
maxDamagePerMob = 999999;
} else if ((monster.getId() >= 9600101) && (monster.getId() <= 9600136)) {
maxDamagePerMob = 888888;
}
if (maxDamagePerMob > 999999) {
maxDamagePerMob = chr.getMaxDamageOver(skill.getId());
} else if (elemMaxDamagePerMob <= 0.0D) {
maxDamagePerMob = 1;
}
return maxDamagePerMob;
}
private static double ElementalStaffAttackBonus(Element elem, double elemMaxDamagePerMob, PlayerStats stats) {
switch (elem) {
case FIRE:
return elemMaxDamagePerMob / 100.0D * (stats.element_fire + stats.getElementBoost(elem));
case ICE:
return elemMaxDamagePerMob / 100.0D * (stats.element_ice + stats.getElementBoost(elem));
case LIGHTING:
return elemMaxDamagePerMob / 100.0D * (stats.element_light + stats.getElementBoost(elem));
case POISON:
return elemMaxDamagePerMob / 100.0D * (stats.element_psn + stats.getElementBoost(elem));
}
return elemMaxDamagePerMob / 100.0D * (stats.def + stats.getElementBoost(elem));
}
/**
* 处理敛财术
* @param player
* @param mob
* @param oned
*/
private static void handlePickPocket(MapleCharacter player, MapleMonster mob, AttackPair oned) {
int maxmeso = player.getBuffedValue(MapleBuffStat.敛财术);
for (Pair eachde : oned.attack) {
Integer eachd = (Integer) eachde.left;
if ((player.getStat().pickRate >= 100) || (Randomizer.nextInt(99) < player.getStat().pickRate)) {
player.getMap().spawnMesoDrop(Math.min((int) Math.max(eachd / 20000.0D * maxmeso, 1.0D), maxmeso), new Point((int) (mob.getTruePosition().getX() + Randomizer.nextInt(100) - 50.0D), (int) mob.getTruePosition().getY()), mob, player, false, (byte) 0);
}
}
}
private static int CalculateMaxWeaponDamagePerHit(MapleCharacter player, MapleMonster monster, AttackInfo attack, Skill theSkill, MapleStatEffect attackEffect, double maxDamagePerMonster, Integer CriticalDamagePercent) {
int dLevel = Math.max(monster.getStats().getLevel() - player.getLevel(), 0) * 2;
int HitRate = Math.min((int) Math.floor(Math.sqrt(player.getStat().getAccuracy())) - (int) Math.floor(Math.sqrt(monster.getStats().getEva())) + 100, 100);
if (dLevel > HitRate) {
HitRate = dLevel;
}
HitRate -= dLevel;
if ((HitRate <= 0) && ((!GameConstants.is新手职业(attack.skillId / 10000)) || (attack.skillId % 10000 != 1000)) && (!GameConstants.isPyramidSkill(attack.skillId)) && (!GameConstants.isMulungSkill(attack.skillId)) && (!GameConstants.isInflationSkill(attack.skillId))) {
return 0;
}
List<Element> elements = new ArrayList<>();
int CritPercent = CriticalDamagePercent;
int PDRate = monster.getStats().getPDRate();
MonsterStatusEffect pdr = monster.getBuff(MonsterStatus.物防);
if (pdr != null) {
PDRate += pdr.getX();
}
double elemMaxDamagePerMonster = maxDamagePerMonster;
if (theSkill != null) {
if (theSkill.getId() == 1321015) {
PDRate = monster.getStats().isBoss() ? PDRate : 0;
}
elements.add(theSkill.getElement());
if (player.getBuffedValue(MapleBuffStat.属性攻击) != null) {
int chargeSkillId = player.getBuffSource(MapleBuffStat.属性攻击);
switch (chargeSkillId) {
case 1201011:
elements.add(Element.FIRE);
break;
case 1201012:
case 21101006:
elements.add(Element.ICE);
break;
case 1211008:
elements.add(Element.LIGHTING);
break;
case 1221004:
elements.add(Element.HOLY);
}
}
double elementalEffect;
if (elements.size() > 0) {
switch (attack.skillId) {
case 3111003:
elementalEffect = attackEffect.getX() / 100.0D;
break;
default:
elementalEffect = 0.5D / elements.size();
}
for (Element element : elements) {
switch (monster.getEffectiveness(element)) {
case IMMUNE:
elemMaxDamagePerMonster = 1.0D;
break;
case WEAK:
elemMaxDamagePerMonster *= (1.0D + elementalEffect + player.getStat().getElementBoost(element));
break;
case STRONG:
elemMaxDamagePerMonster *= (1.0D - elementalEffect - player.getStat().getElementBoost(element));
}
}
}
elemMaxDamagePerMonster -= elemMaxDamagePerMonster * (Math.max(PDRate - Math.max(player.getStat().getIgnoreMobpdpR(attack.skillId), 0) - Math.max(attackEffect == null ? 0 : attackEffect.getIgnoreMob(), 0), 0) / 100.0D);
elemMaxDamagePerMonster += elemMaxDamagePerMonster / 100.0D * CritPercent;
elemMaxDamagePerMonster += elemMaxDamagePerMonster * player.getDamageIncrease(monster.getObjectId()) / 100.0D;
elemMaxDamagePerMonster *= ((monster.getStats().isBoss()) && (attackEffect != null) ? player.getStat().getBossDamageRate(attack.skillId) + attackEffect.getBossDamage() + attackEffect.getDamage() : player.getStat().getDamageRate()) / 100.0D;
} else if (attack.skillId == 0) {
elemMaxDamagePerMonster -= elemMaxDamagePerMonster * (Math.max(PDRate - Math.max(player.getStat().getIgnoreMobpdpR(attack.skillId), 0), 0) / 100.0D);
elemMaxDamagePerMonster += elemMaxDamagePerMonster / 100.0D * CritPercent;
elemMaxDamagePerMonster *= (monster.getStats().isBoss() ? player.getStat().getBossDamageRate(attack.skillId) : player.getStat().getDamageRate()) / 100.0D;
}
int maxDamageOver = player.getMaxDamageOver(attack.skillId);
int maxDamagePerMob = (int) elemMaxDamagePerMonster;
if (theSkill != null) {
if (GameConstants.is新手职业(theSkill.getId() / 10000)) {
switch (theSkill.getId() % 10000) {
case 1000:
maxDamagePerMob = 40;
break;
case 1020:
maxDamagePerMob = 1;
break;
case 1009:
maxDamagePerMob = (int) (monster.getStats().isBoss() ? monster.getMobMaxHp() / 30L * 100L : monster.getMobMaxHp());
}
}
switch (theSkill.getId()) {
case 1311011:
maxDamagePerMob = (int) (maxDamagePerMob * ((attackEffect.getY() - attackEffect.getDamage()) / 100.0D));
break;
case 1321012:
maxDamagePerMob = (int) (maxDamagePerMob * 1.5D);
break;
case 32001000:
case 32101000:
case 32111002:
case 32121002:
maxDamagePerMob = (int) (maxDamagePerMob * 1.5D);
break;
case 61121102:
case 61121203:
if (monster.getStats().isBoss()) {
break;
}
//maxDamagePerMob = 999999;
break;
case 1221011:
maxDamagePerMob = (int) (monster.getStats().isBoss() ? maxDamagePerMob : monster.getHp() - 1L);
break;
case 21120006:
maxDamagePerMob = (int) (monster.getStats().isBoss() ? maxDamagePerMob : monster.getHp() - 1L);
break;
case 3221007:
case 5221016:
case 5721006:
case 27121303:
if (monster.getStats().isBoss()) {
maxDamagePerMob = (int) (maxDamagePerMob * (1.0D + attackEffect.getIgnoreMob() / 100.0D));
} else {
maxDamagePerMob = maxDamageOver;
}
}
}
if ((player.getJob() == 311) || (player.getJob() == 312) || (player.getJob() == 321) || (player.getJob() == 322)) {
Skill mortal = SkillFactory.getSkill((player.getJob() == 311) || (player.getJob() == 312) ? 3110001 : 3210001);
if (player.getTotalSkillLevel(mortal) > 0) {
MapleStatEffect mort = mortal.getEffect(player.getTotalSkillLevel(mortal));
if ((mort != null) && (monster.getHPPercent() < mort.getX())) {
//maxDamagePerMob = 999999;
if (mort.getZ() > 0) {
player.addHP(player.getStat().getMaxHp() * mort.getZ() / 100);
}
}
}
} else if ((player.getJob() == 221) || (player.getJob() == 222)) {
Skill mortal = SkillFactory.getSkill(2210000);
if (player.getTotalSkillLevel(mortal) > 0) {
MapleStatEffect mort = mortal.getEffect(player.getTotalSkillLevel(mortal));
if ((mort != null) && (monster.getHPPercent() < mort.getX())) {
//maxDamagePerMob = 999999;
}
}
}
if ((monster.getId() >= 9400900) && (monster.getId() <= 9400911)) {
//maxDamagePerMob = 999999;
} else if ((monster.getId() >= 9600101) && (monster.getId() <= 9600136)) {
//maxDamagePerMob = 888888;
} else if (maxDamagePerMob > maxDamageOver) {
maxDamagePerMob = maxDamageOver;
} else if (maxDamagePerMob <= 0) {
maxDamagePerMob = 1;
}
return maxDamagePerMob;
}
public static AttackInfo DivideAttack(AttackInfo attack, int rate) {
attack.real = false;
if (rate <= 1) {
return attack;
}
for (AttackPair p : attack.allDamage) {
if (p.attack != null) {
for (Pair<Integer, Boolean> eachd : p.attack) {
eachd.left /= rate; //too ex.
}
}
}
return attack;
}
public static AttackInfo Modify_AttackCrit(AttackInfo attack, MapleCharacter chr, int type, MapleStatEffect effect) {
int criticalRate;
boolean shadow;
List damages;
List damage;
if (attack.skillId != 独行客.金钱炸弹) {
criticalRate = chr.getStat().passive_sharpeye_rate() + (effect == null ? 0 : effect.getCritical());
shadow = (chr.getBuffedValue(MapleBuffStat.影分身) != null) && ((type == 1) || (type == 2));
damages = new ArrayList();
damage = new ArrayList();
for (AttackPair p : attack.allDamage) {
if (p.attack != null) {
int hit = 0;
int mid_att = shadow ? p.attack.size() / 2 : p.attack.size();
int toCrit = (attack.skillId == 4221014) || (attack.skillId == 4221016) || (attack.skillId == 3221007) || (attack.skillId == 4321006) || (attack.skillId == 4331006) ? mid_att : 0;
if (toCrit == 0) {
for (Pair eachd : p.attack) {
if ((!((Boolean) eachd.right)) && (hit < mid_att)) {
if ((((Integer) eachd.left) > 999999) || (Randomizer.nextInt(100) < criticalRate)) {
toCrit++;
}
damage.add(eachd.left);
}
hit++;
}
if (toCrit == 0) {
damage.clear();
continue;
}
Collections.sort(damage);
for (int i = damage.size(); i > damage.size() - toCrit; i--) {
damages.add(damage.get(i - 1));
}
damage.clear();
}
hit = 0;
for (Pair eachd : p.attack) {
if (!((Boolean) eachd.right)) {
if (attack.skillId == 4221014) {
eachd.right = hit == 3;
} else if ((attack.skillId == 3221007) || (attack.skillId == 4321006) || (attack.skillId == 4331006) || (((Integer) eachd.left) > 999999)) {
eachd.right = true;
} else if (hit >= mid_att) {
eachd.right = ((Pair) p.attack.get(hit - mid_att)).right;
} else {
eachd.right = damages.contains(eachd.left);
}
}
hit++;
}
damages.clear();
}
}
}
return attack;
}
/**
* 解析魔法攻击
* @param lea
* @param chr
* @return
*/
public static AttackInfo parseMagicDamage(SeekableLittleEndianAccessor lea, MapleCharacter chr) {
// 1C 11 6C 88 1E 00 00 1D 06 A1 86 01 00 06 80 00 00 F7 FF F6 00 F7 FF F6 00 79 02 09 00 00 00 4C FF F6 00
AttackInfo ret = new AttackInfo();
ret.isMagicAttack = true;
ret.numAttackedAndDamage = lea.readByte();
ret.numAttacked = (byte) (ret.numAttackedAndDamage >>> 4 & 0xF);
ret.numDamage = (byte) (ret.numAttackedAndDamage & 0xF);
ret.skillId = lea.readInt();
lea.skip(1);
ret.stance = lea.readByte();
ret.direction = lea.readByte();
ret.allDamage = new ArrayList();
for (int i = 0; i < ret.numAttacked; i++) {
int oid = lea.readInt();
lea.skip(4); // skip 4 (int)
lea.readShort(); // skip 2 (x - short)
lea.readShort(); // skip 2 (y - short)
lea.skip(6); // skip 4 (int)
List allDamageNumbers = new ArrayList();
for (int j = 0; j < ret.numDamage; j++) {
int damage = lea.readInt();
if (chr.isShowPacket()) {
chr.dropMessage(0, "魔法攻击 - 打怪数量: " + ret.numAttacked + " 打怪次数: " + ret.numDamage + " 怪物OID " + oid + " 伤害: " + damage);
}
if (damage < 0) {
FileoutputUtil.log(FileoutputUtil.攻击出错, "魔法攻击出错封包: 打怪数量: " + ret.numAttacked + " 打怪次数: " + ret.numDamage + " 怪物OID " + oid + " 伤害: " + damage + " 技能ID: " + ret.skillId + lea.toString(true));
}
allDamageNumbers.add(new Pair(damage, false));
}
ret.allDamage.add(new AttackPair(oid, allDamageNumbers));
}
ret.position = lea.readPos();
return ret;
}
/**
* 解析攻击参数
* @param lea
* @param chr
* @return
*/
public static AttackInfo parseCloseRangeAttack(SeekableLittleEndianAccessor lea, MapleCharacter chr) {
// 1A 11 2C 46 0F 00 00 10 04 A2 86 01 00 06 80 00 00 94 FF F6 00 95 FF F6 00 31 01 0B 00 00 00 57 FF F6 00
// 1A 11 2C 46 0F 00 00 14 08 A7 86 01 00 06 00 01 00 39 FF F6 00 38 FF F6 00 C1 01 2E 00 00 00 23 FF 05 01
// 1A 01 00 00 00 00 00 06 04 FD FE 13 01
// 1A 00 3E 41 40 00 00 35 04 D9 01 5F 00 00 63 02 金钱炸弹
AttackInfo ret = new AttackInfo();
ret.isCloseRangeAttack = true;
ret.numAttackedAndDamage = lea.readByte();
ret.numAttacked = (byte) (ret.numAttackedAndDamage >>> 4 & 0xF);//次数
ret.numDamage = (byte) (ret.numAttackedAndDamage & 0xF);//数量
ret.skillId = lea.readInt();
lea.skip(1);
ret.stance = lea.readByte();
ret.direction = lea.readByte();
ret.allDamage = new ArrayList();
for (int i = 0; i < ret.numAttacked; i++) {
int oid = lea.readInt();
lea.skip(4);
lea.readShort();
lea.readShort();
lea.skip(4);
lea.skip(2);
List allDamageNumbers = new ArrayList();
for (int j = 0; j < ret.numDamage; j++) {
int damage = lea.readInt();
if (chr.isShowPacket()) {
chr.dropMessage(0, "近距离攻击 - 打怪数量: " + ret.numAttacked + " 打怪次数: " + ret.numDamage + " 怪物OID " + oid + " 伤害: " + damage);
}
allDamageNumbers.add(new Pair(damage, false));
}
ret.allDamage.add(new AttackPair(oid, allDamageNumbers));
}
ret.position = lea.readPos();
return ret;
}
public static AttackInfo parseRangedAttack(SeekableLittleEndianAccessor lea, MapleCharacter chr) {
// 1B 11 AC CA 2D 00 00 16 06 02 00 41 A1 86 01 00 06 80 00 00 4E FF F6 00 4E FF F6 00 44 02 39 00 00 00 B7 FE 13 01
// 1B
// 01
// 00 00 00 00
// 00
// 96 06
// 13 00 41 36 00 2F 00
// 1B 11 8D 93 3E 00 00 9A 06 02 00 41 A2 86 01 00 06 00 00 00 57 FF F6 00 52 FF F6 00 4A 02 80 00 00 00 F3 FF F6 00 生命吸收
AttackInfo ret = new AttackInfo();
ret.isRangedAttack = true;
ret.numAttackedAndDamage = lea.readByte();
ret.numAttacked = (byte) (ret.numAttackedAndDamage >>> 4 & 0xF);//数量
ret.numDamage = (byte) (ret.numAttackedAndDamage & 0xF);//次数
ret.skillId = lea.readInt();
if (chr.isShowPacket()) {
chr.dropSpouseMessage(1, "[RangedAttack] - 技能ID: " + ret.skillId + " 打怪数量: " + ret.numAttacked + " 打怪次数: " + ret.numDamage);
}
lea.skip(1);
ret.stance = lea.readByte();
ret.direction = lea.readByte();
switch (ret.skillId) {
case 5310011:
case 3121013:
case 5220023:
case 95001000:
case 5221022:
lea.skip(8);
break;
case 5311010:
lea.skip(4);
}
ret.starSlot = lea.readShort();
lea.skip(1);
ret.allDamage = new ArrayList();
for (int i = 0; i < ret.numAttacked; i++) {
int oid = lea.readInt();
lea.skip(14);
List allDamageNumbers = new ArrayList();
for (int j = 0; j < ret.numDamage; j++) {
int damage = lea.readInt();
if (chr.isShowPacket()) {
chr.dropMessage(-5, "远距离攻击 - 打怪数量: " + ret.numAttacked + " 打怪次数: " + ret.numDamage + " 怪物OID " + oid + " 伤害: " + damage);
}
if ((damage > chr.getMaxDamageOver(ret.skillId)) || (damage < 0)) {
if (chr.isShowPacket()) {
chr.dropMessage(-5, "远距离攻击次数出错: 技能ID: " + ret.skillId + " 打怪数量: " + ret.numAttacked + " 打怪次数: " + ret.numDamage + " 怪物OID " + oid + " 伤害: " + damage);
}
// if ((damage > chr.getMaxDamageOver(ret.skillId)) && (!chr.isGM())) {
// chr.sendPolice("系统检测到您的攻击伤害异常,系统对您进行掉线处理。");
// WorldBroadcastService.getInstance().broadcastGMMessage(MaplePacketCreator.serverNotice(6, "[GM Message] "+chr.getName()+" ID: "+chr.getId()+" (等级 "+chr.getLevel()+") 远距离攻击伤害异常。打怪伤害: "+damage+" 地图ID: "+chr.getMapId()));
// }
FileoutputUtil.log(FileoutputUtil.攻击出错, "远距离攻击出错封包: 打怪数量: " + ret.numAttacked + " 打怪次数: " + ret.numDamage + " 怪物OID " + oid + " 伤害: " + damage + " 技能ID: " + ret.skillId + lea.toString(true));
}
allDamageNumbers.add(new Pair(damage, false));
}
ret.allDamage.add(new AttackPair(oid, allDamageNumbers));
}
ret.position = lea.readPos();
if (lea.available() == 4L) {
ret.skillposition = lea.readPos();
}
return ret;
}
public static AttackInfo parseMesoExplosion(SeekableLittleEndianAccessor lea, AttackInfo ret, MapleCharacter chr) {
if (ret.numDamage == 0) {
lea.skip(4);
byte bullets = lea.readByte();
for (int j = 0; j < bullets; j++) {
int mesoid = lea.readInt();
lea.skip(2);
if (chr.isShowPacket()) {
chr.dropMessage(-5, "金钱炸弹攻击怪物: 无怪 " + ret.numDamage + " 金币ID: " + mesoid);
}
ret.allDamage.add(new AttackPair(mesoid, null));
}
lea.skip(2);
return ret;
}
for (int i = 0; i < ret.numAttacked; i++) {
int oid = lea.readInt();
lea.skip(19);
byte bullets = lea.readByte();
List allDamageNumbers = new ArrayList();
for (int j = 0; j < bullets; j++) {
int damage = lea.readInt();
if (chr.isShowPacket()) {
chr.dropMessage(-5, "金钱炸弹攻击怪物: " + ret.numAttacked + " 攻击次数: " + bullets + " 打怪伤害: " + damage);
}
allDamageNumbers.add(new Pair(damage, false));
}
ret.allDamage.add(new AttackPair(oid, allDamageNumbers));
lea.skip(4);
lea.skip(4);
lea.skip(4);
}
lea.skip(4);
byte bullets = lea.readByte();
for (int j = 0; j < bullets; j++) {
int mesoid = lea.readInt();
lea.skip(2);
if (chr.isShowPacket()) {
chr.dropMessage(-5, "金钱炸弹攻击怪物: 有怪 " + bullets + " 金币ID: " + mesoid);
}
ret.allDamage.add(new AttackPair(mesoid, null));
}
return ret;
}
}