Search in sources :

Example 1 with ElementalEffectiveness

use of server.life.ElementalEffectiveness in project HeavenMS by ronancpl.

the class AbstractDealDamageHandler method parseDamage.

protected AttackInfo parseDamage(LittleEndianAccessor lea, MapleCharacter chr, boolean ranged, boolean magic) {
    // 2C 00 00 01 91 A1 12 00 A5 57 62 FC E2 75 99 10 00 47 80 01 04 01 C6 CC 02 DD FF 5F 00
    AttackInfo ret = new AttackInfo();
    lea.readByte();
    ret.numAttackedAndDamage = lea.readByte();
    ret.numAttacked = (ret.numAttackedAndDamage >>> 4) & 0xF;
    ret.numDamage = ret.numAttackedAndDamage & 0xF;
    ret.allDamage = new HashMap<>();
    ret.skill = lea.readInt();
    ret.ranged = ranged;
    ret.magic = magic;
    if (ret.skill > 0) {
        ret.skilllevel = chr.getSkillLevel(ret.skill);
        if (ret.skilllevel == 0 && GameConstants.isPqSkillMap(chr.getMapId()) && GameConstants.isPqSkill(ret.skill))
            ret.skilllevel = 1;
    }
    if (ret.skill == Evan.ICE_BREATH || ret.skill == Evan.FIRE_BREATH || ret.skill == FPArchMage.BIG_BANG || ret.skill == ILArchMage.BIG_BANG || ret.skill == Bishop.BIG_BANG || ret.skill == Gunslinger.GRENADE || ret.skill == Brawler.CORKSCREW_BLOW || ret.skill == ThunderBreaker.CORKSCREW_BLOW || ret.skill == NightWalker.POISON_BOMB) {
        ret.charge = lea.readInt();
    } else {
        ret.charge = 0;
    }
    if (ret.skill == Paladin.HEAVENS_HAMMER) {
        ret.isHH = true;
    } else if (ret.skill == Aran.COMBO_TEMPEST) {
        ret.isTempest = true;
    }
    lea.skip(8);
    ret.display = lea.readByte();
    ret.direction = lea.readByte();
    ret.stance = lea.readByte();
    if (ret.skill == ChiefBandit.MESO_EXPLOSION) {
        if (ret.numAttackedAndDamage == 0) {
            lea.skip(10);
            int bullets = lea.readByte();
            for (int j = 0; j < bullets; j++) {
                int mesoid = lea.readInt();
                lea.skip(1);
                ret.allDamage.put(Integer.valueOf(mesoid), null);
            }
            return ret;
        } else {
            lea.skip(6);
        }
        for (int i = 0; i < ret.numAttacked + 1; i++) {
            int oid = lea.readInt();
            if (i < ret.numAttacked) {
                lea.skip(12);
                int bullets = lea.readByte();
                List<Integer> allDamageNumbers = new ArrayList<>();
                for (int j = 0; j < bullets; j++) {
                    int damage = lea.readInt();
                    allDamageNumbers.add(Integer.valueOf(damage));
                }
                ret.allDamage.put(Integer.valueOf(oid), allDamageNumbers);
                lea.skip(4);
            } else {
                int bullets = lea.readByte();
                for (int j = 0; j < bullets; j++) {
                    int mesoid = lea.readInt();
                    lea.skip(1);
                    ret.allDamage.put(Integer.valueOf(mesoid), null);
                }
            }
        }
        return ret;
    }
    if (ranged) {
        lea.readByte();
        ret.speed = lea.readByte();
        lea.readByte();
        ret.rangedirection = lea.readByte();
        lea.skip(7);
        if (ret.skill == Bowmaster.HURRICANE || ret.skill == Marksman.PIERCING_ARROW || ret.skill == Corsair.RAPID_FIRE || ret.skill == WindArcher.HURRICANE) {
            lea.skip(4);
        }
    } else {
        lea.readByte();
        ret.speed = lea.readByte();
        lea.skip(4);
    }
    // Find the base damage to base futher calculations on.
    // Several skills have their own formula in this section.
    int calcDmgMax = 0;
    if (magic && ret.skill != 0) {
        calcDmgMax = (chr.getTotalMagic() * chr.getTotalMagic() / 1000 + chr.getTotalMagic()) / 30 + chr.getTotalInt() / 200;
    } else if (ret.skill == 4001344 || ret.skill == NightWalker.LUCKY_SEVEN || ret.skill == NightLord.TRIPLE_THROW) {
        calcDmgMax = (chr.getTotalLuk() * 5) * chr.getTotalWatk() / 100;
    } else if (ret.skill == DragonKnight.DRAGON_ROAR) {
        calcDmgMax = (chr.getTotalStr() * 4 + chr.getTotalDex()) * chr.getTotalWatk() / 100;
    } else if (ret.skill == NightLord.VENOMOUS_STAR || ret.skill == Shadower.VENOMOUS_STAB) {
        calcDmgMax = (int) (18.5 * (chr.getTotalStr() + chr.getTotalLuk()) + chr.getTotalDex() * 2) / 100 * chr.calculateMaxBaseDamage(chr.getTotalWatk());
    } else {
        calcDmgMax = chr.calculateMaxBaseDamage(chr.getTotalWatk());
    }
    if (ret.skill != 0) {
        Skill skill = SkillFactory.getSkill(ret.skill);
        MapleStatEffect effect = skill.getEffect(ret.skilllevel);
        if (magic) {
            // Since the skill is magic based, use the magic formula
            if (chr.getJob() == MapleJob.IL_ARCHMAGE || chr.getJob() == MapleJob.IL_MAGE) {
                int skillLvl = chr.getSkillLevel(ILMage.ELEMENT_AMPLIFICATION);
                if (skillLvl > 0)
                    calcDmgMax = calcDmgMax * SkillFactory.getSkill(ILMage.ELEMENT_AMPLIFICATION).getEffect(skillLvl).getY() / 100;
            } else if (chr.getJob() == MapleJob.FP_ARCHMAGE || chr.getJob() == MapleJob.FP_MAGE) {
                int skillLvl = chr.getSkillLevel(FPMage.ELEMENT_AMPLIFICATION);
                if (skillLvl > 0)
                    calcDmgMax = calcDmgMax * SkillFactory.getSkill(FPMage.ELEMENT_AMPLIFICATION).getEffect(skillLvl).getY() / 100;
            } else if (chr.getJob() == MapleJob.BLAZEWIZARD3 || chr.getJob() == MapleJob.BLAZEWIZARD4) {
                int skillLvl = chr.getSkillLevel(BlazeWizard.ELEMENT_AMPLIFICATION);
                if (skillLvl > 0)
                    calcDmgMax = calcDmgMax * SkillFactory.getSkill(BlazeWizard.ELEMENT_AMPLIFICATION).getEffect(skillLvl).getY() / 100;
            } else if (chr.getJob() == MapleJob.EVAN7 || chr.getJob() == MapleJob.EVAN8 || chr.getJob() == MapleJob.EVAN9 || chr.getJob() == MapleJob.EVAN10) {
                int skillLvl = chr.getSkillLevel(Evan.MAGIC_AMPLIFICATION);
                if (skillLvl > 0)
                    calcDmgMax = calcDmgMax * SkillFactory.getSkill(Evan.MAGIC_AMPLIFICATION).getEffect(skillLvl).getY() / 100;
            }
            calcDmgMax *= effect.getMatk();
            if (ret.skill == Cleric.HEAL) {
                // This formula is still a bit wonky, but it is fairly accurate.
                calcDmgMax = (int) Math.round((chr.getTotalInt() * 4.8 + chr.getTotalLuk() * 4) * chr.getTotalMagic() / 1000);
                calcDmgMax = calcDmgMax * effect.getHp() / 100;
            }
        } else if (ret.skill == Hermit.SHADOW_MESO) {
            // Shadow Meso also has its own formula
            calcDmgMax = effect.getMoneyCon() * 10;
            calcDmgMax = (int) Math.floor(calcDmgMax * 1.5);
        } else {
            // Normal damage formula for skills
            calcDmgMax = calcDmgMax * effect.getDamage() / 100;
        }
    }
    Integer comboBuff = chr.getBuffedValue(MapleBuffStat.COMBO);
    if (comboBuff != null && comboBuff > 0) {
        int oid = chr.isCygnus() ? DawnWarrior.COMBO : Crusader.COMBO;
        int advcomboid = chr.isCygnus() ? DawnWarrior.ADVANCED_COMBO : Hero.ADVANCED_COMBO;
        if (comboBuff > 6) {
            // Advanced Combo
            MapleStatEffect ceffect = SkillFactory.getSkill(advcomboid).getEffect(chr.getSkillLevel(advcomboid));
            calcDmgMax = (int) Math.floor(calcDmgMax * (ceffect.getDamage() + 50) / 100 + 0.20 + (comboBuff - 5) * 0.04);
        } else {
            // Normal Combo
            int skillLv = chr.getSkillLevel(oid);
            if (skillLv <= 0 || chr.isGM())
                skillLv = SkillFactory.getSkill(oid).getMaxLevel();
            if (skillLv > 0) {
                MapleStatEffect ceffect = SkillFactory.getSkill(oid).getEffect(skillLv);
                calcDmgMax = (int) Math.floor(calcDmgMax * (ceffect.getDamage() + 50) / 100 + Math.floor((comboBuff - 1) * (skillLv / 6)) / 100);
            }
        }
        if (GameConstants.isFinisherSkill(ret.skill)) {
            // Finisher skills do more damage based on how many orbs the player has.
            int orbs = comboBuff - 1;
            if (orbs == 2)
                calcDmgMax *= 1.2;
            else if (orbs == 3)
                calcDmgMax *= 1.54;
            else if (orbs == 4)
                calcDmgMax *= 2;
            else if (orbs >= 5)
                calcDmgMax *= 2.5;
        }
    }
    if (chr.getEnergyBar() == 15000) {
        int energycharge = chr.isCygnus() ? ThunderBreaker.ENERGY_CHARGE : Marauder.ENERGY_CHARGE;
        MapleStatEffect ceffect = SkillFactory.getSkill(energycharge).getEffect(chr.getSkillLevel(energycharge));
        calcDmgMax *= ceffect.getDamage() / 100;
    }
    if (chr.getMapId() >= 914000000 && chr.getMapId() <= 914000500) {
        // Aran Tutorial.
        calcDmgMax += 80000;
    }
    boolean canCrit = false;
    if (chr.getJob().isA((MapleJob.BOWMAN)) || chr.getJob().isA(MapleJob.THIEF) || chr.getJob().isA(MapleJob.NIGHTWALKER1) || chr.getJob().isA(MapleJob.WINDARCHER1) || chr.getJob() == MapleJob.ARAN3 || chr.getJob() == MapleJob.ARAN4 || chr.getJob() == MapleJob.MARAUDER || chr.getJob() == MapleJob.BUCCANEER) {
        canCrit = true;
    }
    if (chr.getBuffEffect(MapleBuffStat.SHARP_EYES) != null) {
        // Any class that has sharp eyes can crit. Also, since it stacks with normal crit go ahead
        // and calc it in.
        canCrit = true;
        calcDmgMax *= 1.4;
    }
    boolean shadowPartner = false;
    if (chr.getBuffEffect(MapleBuffStat.SHADOWPARTNER) != null) {
        shadowPartner = true;
    }
    if (ret.skill != 0) {
        int fixed = ret.getAttackEffect(chr, SkillFactory.getSkill(ret.skill)).getFixDamage();
        if (fixed > 0)
            calcDmgMax = fixed;
    }
    for (int i = 0; i < ret.numAttacked; i++) {
        int oid = lea.readInt();
        lea.skip(14);
        List<Integer> allDamageNumbers = new ArrayList<>();
        MapleMonster monster = chr.getMap().getMonsterByOid(oid);
        if (chr.getBuffEffect(MapleBuffStat.WK_CHARGE) != null) {
            // Charge, so now we need to check elemental effectiveness
            int sourceID = chr.getBuffSource(MapleBuffStat.WK_CHARGE);
            int level = chr.getBuffedValue(MapleBuffStat.WK_CHARGE);
            if (monster != null) {
                if (sourceID == WhiteKnight.BW_FIRE_CHARGE || sourceID == WhiteKnight.SWORD_FIRE_CHARGE) {
                    if (monster.getStats().getEffectiveness(Element.FIRE) == ElementalEffectiveness.WEAK) {
                        calcDmgMax *= 1.05 + level * 0.015;
                    }
                } else if (sourceID == WhiteKnight.BW_ICE_CHARGE || sourceID == WhiteKnight.SWORD_ICE_CHARGE) {
                    if (monster.getStats().getEffectiveness(Element.ICE) == ElementalEffectiveness.WEAK) {
                        calcDmgMax *= 1.05 + level * 0.015;
                    }
                } else if (sourceID == WhiteKnight.BW_LIT_CHARGE || sourceID == WhiteKnight.SWORD_LIT_CHARGE) {
                    if (monster.getStats().getEffectiveness(Element.LIGHTING) == ElementalEffectiveness.WEAK) {
                        calcDmgMax *= 1.05 + level * 0.015;
                    }
                } else if (sourceID == Paladin.BW_HOLY_CHARGE || sourceID == Paladin.SWORD_HOLY_CHARGE) {
                    if (monster.getStats().getEffectiveness(Element.HOLY) == ElementalEffectiveness.WEAK) {
                        calcDmgMax *= 1.2 + level * 0.015;
                    }
                }
            } else {
                // Since we already know the skill has an elemental attribute, but we dont know if the monster is weak or not, lets
                // take the safe approach and just assume they are weak.
                calcDmgMax *= 1.5;
            }
        }
        if (ret.skill != 0) {
            Skill skill = SkillFactory.getSkill(ret.skill);
            if (skill.getElement() != Element.NEUTRAL && chr.getBuffedValue(MapleBuffStat.ELEMENTAL_RESET) == null) {
                // The skill has an element effect, so we need to factor that in.
                if (monster != null) {
                    ElementalEffectiveness eff = monster.getElementalEffectiveness(skill.getElement());
                    if (eff == ElementalEffectiveness.WEAK) {
                        calcDmgMax *= 1.5;
                    } else if (eff == ElementalEffectiveness.STRONG) {
                    // calcDmgMax *= 0.5;
                    }
                } else {
                    // Since we already know the skill has an elemental attribute, but we dont know if the monster is weak or not, lets
                    // take the safe approach and just assume they are weak.
                    calcDmgMax *= 1.5;
                }
            }
            if (ret.skill == FPWizard.POISON_BREATH || ret.skill == FPMage.POISON_MIST || ret.skill == FPArchMage.FIRE_DEMON || ret.skill == ILArchMage.ICE_DEMON) {
                if (monster != null) {
                // Turns out poison is completely server side, so I don't know why I added this. >.<
                // calcDmgMax = monster.getHp() / (70 - chr.getSkillLevel(skill));
                }
            } else if (ret.skill == Hermit.SHADOW_WEB) {
                if (monster != null) {
                    calcDmgMax = monster.getHp() / (50 - chr.getSkillLevel(skill));
                }
            }
        }
        for (int j = 0; j < ret.numDamage; j++) {
            int damage = lea.readInt();
            int hitDmgMax = calcDmgMax;
            if (ret.skill == Buccaneer.BARRAGE) {
                if (j > 3)
                    hitDmgMax *= Math.pow(2, (j - 3));
            }
            if (shadowPartner) {
                // in for the crit effects.
                if (j >= ret.numDamage / 2) {
                    hitDmgMax *= 0.5;
                }
            }
            if (ret.skill == Marksman.SNIPE) {
                damage = 195000 + Randomizer.nextInt(5000);
                hitDmgMax = 200000;
            }
            int maxWithCrit = hitDmgMax;
            if (// They can crit, so up the max.
            canCrit)
                maxWithCrit *= 2;
            // Warn if the damage is over 1.5x what we calculated above.
            if (damage > maxWithCrit * 1.5) {
                AutobanFactory.DAMAGE_HACK.alert(chr, "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");
            }
            // Add a ab point if its over 5x what we calculated.
            if (damage > maxWithCrit * 5) {
                AutobanFactory.DAMAGE_HACK.addPoint(chr.getAutobanManager(), "DMG: " + damage + " MaxDMG: " + maxWithCrit + " SID: " + ret.skill + " MobID: " + (monster != null ? monster.getId() : "null") + " Map: " + chr.getMap().getMapName() + " (" + chr.getMapId() + ")");
            }
            if (ret.skill == Marksman.SNIPE || (canCrit && damage > hitDmgMax)) {
                // If the skill is a crit, inverse the damage to make it show up on clients.
                damage = -Integer.MAX_VALUE + damage - 1;
            }
            allDamageNumbers.add(damage);
        }
        if (ret.skill != Corsair.RAPID_FIRE || ret.skill != Aran.HIDDEN_FULL_DOUBLE || ret.skill != Aran.HIDDEN_FULL_TRIPLE || ret.skill != Aran.HIDDEN_OVER_DOUBLE || ret.skill != Aran.HIDDEN_OVER_TRIPLE) {
            lea.skip(4);
        }
        ret.allDamage.put(Integer.valueOf(oid), allDamageNumbers);
    }
    if (ret.skill == NightWalker.POISON_BOMB) {
        // Poison Bomb
        lea.skip(4);
        ret.position.setLocation(lea.readShort(), lea.readShort());
    }
    return ret;
}
Also used : Skill(client.Skill) MobSkill(server.life.MobSkill) MapleStatEffect(server.MapleStatEffect) ArrayList(java.util.ArrayList) ElementalEffectiveness(server.life.ElementalEffectiveness) Point(java.awt.Point) MapleMonster(server.life.MapleMonster)

Aggregations

Skill (client.Skill)1 Point (java.awt.Point)1 ArrayList (java.util.ArrayList)1 MapleStatEffect (server.MapleStatEffect)1 ElementalEffectiveness (server.life.ElementalEffectiveness)1 MapleMonster (server.life.MapleMonster)1 MobSkill (server.life.MobSkill)1