Пример #1
0
        // Author: wackyracer / Joren McGrew
        // This is the formula for maximum melee damage dealt by any class.
        // TODO: This formula does not take the 3rd job skills into account yet. TODO
        // This formula is from: https://web.archive.org/web/20081009190310/http://www.southperry.net:80/forums/showthread.php?t=855&page=2
        // Formula Name: General Formula
        // MAX = (Primary Stat + Secondary Stat) * Weapon Attack / 100
        // I have added elemental damage amplification/reduction to the formula.
        // I have added mob resistance calculation for damage reduction to the formula.
        // I have added mob level difference from character (if mob level is higher than character's).
        // My formula looks like...
        // MAX = ((Primary Stat + Secondary Stat) * Weapon Attack / 100) * Element Modifier * Level Difference - (Mob Weapon Defense * 0.5)
        public static double MaximumMeleeDamage(Character chr, Mob mob, int Targets = 1, int SkillID = 99)
        {
            var    MeleeSkill = chr.Skills.GetSkillLevelData(SkillID, out byte MeleeSkillLevel);
            string WeaponType;
            double WeaponAttack = chr.PrimaryStats.BuffWeaponAttack.N + chr.Inventory.GetTotalWAttackInEquips(false);

            try                                  // FAILSAFE
            {
                WeaponType = GetWeaponType(chr); // What weapon was used? Sword, Axe, etc.?
                double PrimaryStat   = 0.0;
                double SecondaryStat = 0.0;

                switch (WeaponType)
                {
                case "1HS":     // ONE HANDED SWORD
                    PrimaryStat   = chr.PrimaryStats.GetStrAddition() * 4.0;
                    SecondaryStat = chr.PrimaryStats.GetDexAddition();
                    break;

                case "1HA":     // ONE HANDED AXE
                case "1HBW":    // ONE HANDED BLUNT WEAPON
                case "WAND":    // WAND
                case "STAFF":   // STAFF
                    PrimaryStat   = chr.PrimaryStats.GetStrAddition() * 4.4;
                    SecondaryStat = chr.PrimaryStats.GetDexAddition();
                    break;

                case "2HS":     // TWO HANDED SWORD
                    PrimaryStat   = chr.PrimaryStats.GetStrAddition() * 4.6;
                    SecondaryStat = chr.PrimaryStats.GetDexAddition();
                    break;

                case "2HA":     // TWO HANDED AXE
                case "2HBW":    // TWO HANDED BLUNT WEAPON
                    PrimaryStat   = chr.PrimaryStats.GetStrAddition() * 4.8;
                    SecondaryStat = chr.PrimaryStats.GetDexAddition();
                    break;

                case "SPEAR":  // SPEAR
                case "PA":     // POLE ARM
                    PrimaryStat   = chr.PrimaryStats.GetStrAddition() * 5.0;
                    SecondaryStat = chr.PrimaryStats.GetDexAddition();
                    break;

                case "DAGGERNT":     // NON-THIEF USING DAGGER
                    PrimaryStat   = chr.PrimaryStats.GetStrAddition() * 4.0;
                    SecondaryStat = chr.PrimaryStats.GetDexAddition();
                    break;

                case "DAGGERT":     // THIEF USING DAGGER
                    PrimaryStat   = chr.PrimaryStats.GetLukAddition() * 3.6;
                    SecondaryStat = chr.PrimaryStats.GetStrAddition() + chr.PrimaryStats.GetDexAddition();
                    break;

                case "BOW":     // BOW
                    PrimaryStat   = chr.PrimaryStats.GetDexAddition() * 3.4;
                    SecondaryStat = chr.PrimaryStats.GetStrAddition();
                    break;

                case "XBOW":     // CROSSBOW
                    PrimaryStat   = chr.PrimaryStats.GetDexAddition() * 3.6;
                    SecondaryStat = chr.PrimaryStats.GetStrAddition();
                    break;

                case "CLAW":     // CLAW
                    return((chr.PrimaryStats.GetLukAddition() * 1.0 + chr.PrimaryStats.GetStrAddition() + chr.PrimaryStats.GetDexAddition()) * WeaponAttack / 150.0);

                case "NONE":     // ERROR!
                default:
                    Program.MainForm.LogAppend("Woops! Something went wrong with depicting the Weapon Type!");
                    break;
                }

                if (MeleeSkill != null) // If the player used a melee skill...
                {
                    switch (SkillID)
                    {
                    case 1001004:     // POWER STRIKE
                    case 4201004:     // STEAL
                        return((((PrimaryStat + SecondaryStat) * WeaponAttack / 100.0) * ElementModifier(mob, SkillID) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5)) * MeleeSkill.Damage);

                    case 1001005:     // SLASH BLAST
                        return(((((PrimaryStat + SecondaryStat) * WeaponAttack / 100.0) * ElementModifier(mob, SkillID) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5)) * MeleeSkill.Damage) * Targets);

                    case 4001334:     // DOUBLE STAB
                    case 4201005:     // SAVAGE BLOW
                        return(((((PrimaryStat + SecondaryStat) * WeaponAttack / 100.0) * ElementModifier(mob, SkillID) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5)) * MeleeSkill.Damage) * MeleeSkill.HitCount);

                    default:
                        return(((PrimaryStat + SecondaryStat) * WeaponAttack / 100.0) * ElementModifier(mob, SkillID) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5));
                    }
                }
                else
                {
                    return(((PrimaryStat + SecondaryStat) * WeaponAttack / 100.0) * ElementModifier(mob, SkillID) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5));
                }
            }

            catch (Exception melee)
            {
                Program.MainForm.LogAppend("MELEE ANTI-CHEAT EXCEPTION: " + melee);
            }

            return(0.0);
        }
Пример #2
0
        // Author: wackyracer / Joren McGrew
        // This is the formula for maximum damage dealt by regular ranged attacks.
        // Each class has a different formula for their ranged attacks.
        // The General Formula is: MAX = (Primary Stat + Secondary Stat) * Weapon Attack / 100
        // Including the mob weapon defense resistance calculation, level difference amplification/reduction formulas, and critical strike formula...
        // Something seems off here. I can smell it. I think Bow/Crossbow Mastery needs to be taken into account but I can't find the formula for it online...
        // Guess I can only find out with testing... F3.
        public static double MaximumRangedDamage(Character chr, Mob mob, int SkillID, int StarID, byte Targets, int ClientTotalDamage)
        {
            // If they're a Bowman, Thief, or GM...
            if (chr.PrimaryStats.Job / 100 == 3 || chr.PrimaryStats.Job / 100 == 4 || chr.PrimaryStats.Job / 100 == 5)
            {
                // Bow
                // Primary: DEX * 3.4
                // Secondary: STR
                if (StarID / 1000 == 2060)
                {
                    double chrSTR               = chr.PrimaryStats.GetStrAddition();
                    double chrDEX               = chr.PrimaryStats.GetDexAddition();
                    double chrWeaponAttack      = chr.PrimaryStats.BuffWeaponAttack.N + chr.Inventory.GetTotalWAttackInEquips(true);
                    double MaxDamageWithoutCrit = ((chrDEX * 3.4 + chrSTR) * chrWeaponAttack / 100.0) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5);
                    double MaxDamageWithCrit    = ((chrDEX * 3.4 + chrSTR) * chrWeaponAttack / 100.0) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5) * CriticalStrikeModifier(chr);

                    if (ClientTotalDamage <= MaxDamageWithoutCrit)
                    {
                        switch (SkillID) // NEEDS TESTING
                        {
                        case 3001004:    // Arrow Blow
                            var chrArrowBlowData = chr.Skills.GetSkillLevelData(3001004, out byte ArrowBlowLevel);
                            return(MaxDamageWithoutCrit * (chrArrowBlowData.Damage / 100.0));

                        case 3001005:     // Double Shot
                            var chrDoubleShotData = chr.Skills.GetSkillLevelData(3001005, out byte DoubleShotLevel);
                            return((MaxDamageWithoutCrit * (chrDoubleShotData.Damage / 100.0)) * 2.0);

                        case 3100001:     // Final Attack
                            return(MaxDamageWithoutCrit * (FinalAttackModifier(chr, SkillID)));

                        default:
                            return(MaxDamageWithoutCrit);
                        }
                    }

                    else
                    {
                        switch (SkillID) // NEEDS TESTING
                        {
                        case 3001004:    // Arrow Blow
                            var chrArrowBlowData = chr.Skills.GetSkillLevelData(3001004, out byte ArrowBlowLevel);
                            return(MaxDamageWithCrit * (chrArrowBlowData.Damage / 100.0));

                        case 3001005:     // Double Shot
                            var chrDoubleShotData = chr.Skills.GetSkillLevelData(3001005, out byte DoubleShotLevel);
                            return((MaxDamageWithCrit * (chrDoubleShotData.Damage / 100.0)) * 2.0);

                        case 3100001:     // Final Attack
                            return(MaxDamageWithCrit * (FinalAttackModifier(chr, SkillID)));

                        default:
                            return(MaxDamageWithCrit);
                        }
                    }
                }

                // Crossbow
                // Primary: DEX * 3.6
                // Secondary: STR
                else if (StarID / 1000 == 2061)
                {
                    double chrSTR               = chr.PrimaryStats.GetStrAddition();
                    double chrDEX               = chr.PrimaryStats.GetDexAddition();
                    double chrWeaponAttack      = chr.PrimaryStats.BuffWeaponAttack.N + chr.Inventory.GetTotalWAttackInEquips(true);
                    double MaxDamageWithoutCrit = ((chrDEX * 3.6 + chrSTR) * chrWeaponAttack / 100.0) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5);
                    double MaxDamageWithCrit    = ((chrDEX * 3.6 + chrSTR) * chrWeaponAttack / 100.0) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5) * CriticalStrikeModifier(chr);

                    if (ClientTotalDamage <= MaxDamageWithoutCrit)
                    {
                        switch (SkillID) // NEEDS TESTING
                        {
                        case 3001004:    // Arrow Blow
                            var chrArrowBlowData = chr.Skills.GetSkillLevelData(3001004, out byte ArrowBlowLevel);
                            return(MaxDamageWithoutCrit * (chrArrowBlowData.Damage / 100.0));

                        case 3001005:     // Double Shot
                            var chrDoubleShotData = chr.Skills.GetSkillLevelData(3001005, out byte DoubleShotLevel);
                            return((MaxDamageWithoutCrit * (chrDoubleShotData.Damage / 100.0)) * 2.0);

                        case 3200001:     // Final Attack
                            return(MaxDamageWithoutCrit * (FinalAttackModifier(chr, SkillID)));

                        case 3201005:     // Iron Arrow (needs testing)
                            //var chrIronArrowData = chr.Skills.GetSkillLevelData(3201005, out byte IronArrowLevel);
                            return(MaxDamageWithoutCrit * (10.0 * (1.0 - Math.Pow(0.9, Targets))));

                        default:
                            return(MaxDamageWithoutCrit);
                        }
                    }

                    else
                    {
                        switch (SkillID) // NEEDS TESTING
                        {
                        case 3001004:    // Arrow Blow
                            var chrArrowBlowData = chr.Skills.GetSkillLevelData(3001004, out byte ArrowBlowLevel);
                            return(MaxDamageWithCrit * chrArrowBlowData.Damage);

                        case 3001005:     // Double Shot
                            var chrDoubleShotData = chr.Skills.GetSkillLevelData(3001005, out byte DoubleShotLevel);
                            return((MaxDamageWithCrit * chrDoubleShotData.Damage) * 2.0);

                        case 3200001:     // Final Attack
                            return(MaxDamageWithCrit * (FinalAttackModifier(chr, SkillID)));

                        case 3201005:     // Iron Arrow (needs testing)
                            //var chrIronArrowData = chr.Skills.GetSkillLevelData(3201005, out byte IronArrowLevel);
                            return(MaxDamageWithCrit * (10.0 * (1.0 - Math.Pow(0.9, Targets))));

                        default:
                            return(MaxDamageWithCrit);
                        }
                    }
                }

                // Throwing Stars (Thieves)
                // Primary: LUK * 3.6
                // Secondary: STR + DEX
                else if (StarID / 10000 == 207)
                {
                    double chrSTR               = chr.PrimaryStats.GetStrAddition();
                    double chrDEX               = chr.PrimaryStats.GetDexAddition();
                    double chrLUK               = chr.PrimaryStats.GetLukAddition();
                    double chrWeaponAttack      = chr.PrimaryStats.BuffWeaponAttack.N + chr.Inventory.GetTotalWAttackInEquips(true);
                    double MaxDamageWithoutCrit = ((chrLUK * 3.6 + (chrSTR + chrDEX)) * chrWeaponAttack / 100.0) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5);
                    double MaxDamageWithCrit    = ((chrLUK * 3.6 + (chrSTR + chrDEX)) * chrWeaponAttack / 100.0) * LevelDisadvantageModifierDmg(chr.PrimaryStats.Level, mob.Data.Level) - (mob.Data.PDD * 0.5) * CriticalStrikeModifier(chr);

                    if (chr.PrimaryStats.Job / 10 == 41 || chr.PrimaryStats.Job / 100 == 5)
                    {
                        if (ClientTotalDamage <= MaxDamageWithoutCrit)
                        {                           // NEEDS TESTING
                            if (SkillID == 4101005) // Drain
                            {
                                var chrDrainData = chr.Skills.GetSkillLevelData(4101005, out byte DrainLevel);
                                return(MaxDamageWithoutCrit * (chrDrainData.Damage / 100.0));
                            }
                            else
                            {
                                return(MaxDamageWithoutCrit);
                            }
                        }

                        else
                        {
                            if (SkillID == 4101005) // Drain
                            {
                                var chrDrainData = chr.Skills.GetSkillLevelData(4101005, out byte DrainLevel);
                                return(MaxDamageWithCrit * (chrDrainData.Damage / 100.0));
                            }
                            else
                            {
                                return(MaxDamageWithCrit);
                            }
                        }
                    }

                    else
                    {
                        return(MaxDamageWithoutCrit);
                    }
                }

                else
                {
                    return(0.0); // HAAAAAAAAAAAAAAAAAAAAAAX -throws PC monitor-
                }
            }

            else
            {
                return(0.0); // HAAAAAAAAAAAAAAAAAAAAAAX -throws PC monitor-
            }
        }
Пример #3
0
        public static void HandleCharacterDamage(Character chr, Packet pr)
        {
            //1A FF 03 00 00 00 00 00 00 00 00 04 87 01 00 00 00
            sbyte attack         = pr.ReadSByte();
            int   damage         = pr.ReadInt();
            int   reducedDamage  = damage;
            int   actualHPEffect = -damage;
            int   actualMPEffect = 0;
            int   healSkillId    = 0;
            Mob   mob            = null;

            if (chr.AssertForHack(damage < -1, "Less than -1 (" + damage + ") damage in HandleCharacterDamage"))
            {
                return;
            }

            if (chr.PrimaryStats.HP == 0)
            {
                return;
            }

            byte mobSkillId = 0, mobSkillLevel = 0;

            if (attack <= -2)
            {
                mobSkillLevel = pr.ReadByte();
                mobSkillId    = pr.ReadByte(); // (short >> 8)

                Trace.WriteLine($"Got a hit with {attack} attack, mobSkillLevel {mobSkillLevel}, mobSkillId {mobSkillId}");
            }
            else
            {
                int magicAttackElement = 0;
                if (pr.ReadBool())
                {
                    magicAttackElement = pr.ReadInt();
                    // 0 = no element (Grendel the Really Old, 9001001)
                    // 1 = Ice (Celion? blue, 5120003)
                    // 2 = Lightning (Regular big Sentinel, 3000000)
                    // 3 = Fire (Fire sentinel, 5200002)
                }

                var mobMapId = pr.ReadInt();
                var mobId    = pr.ReadInt();

                mob = chr.Field.GetMob(mobMapId);
                if (mob == null ||
                    mobId != mob.MobID)
                {
                    return;
                }

                // Newer ver: int nCalcDamageMobStatIndex
                var stance      = pr.ReadByte();
                var isReflected = pr.ReadBool();

                byte  reflectHitAction = 0;
                short reflectX = 0, reflectY = 0;
                if (isReflected)
                {
                    reflectHitAction = pr.ReadByte();
                    reflectX         = pr.ReadShort();
                    reflectY         = pr.ReadShort();
                }

                if (chr.PrimaryStats.BuffMagicGuard.HasReferenceId(Constants.Magician.Skills.MagicGuard) &&
                    chr.PrimaryStats.MP > 0)
                {
                    // Absorbs X amount of damage. :)
                    var  skillId = chr.PrimaryStats.BuffMagicGuard.R;
                    byte skillLevel;
                    var  sld = chr.Skills.GetSkillLevelData(skillId, out skillLevel);

                    int damageEaten = (int)Math.Round((damage * (sld.XValue / 100.0d)));

                    // MagicGuard doesn't show reduced damage.


                    Trace.WriteLine($"Reducing damage by MG. Reflected {damageEaten}");

                    //Program.MainForm.LogAppend("MG Damage before change: " + actualHPEffect);
                    actualHPEffect += damageEaten;
                    //Program.MainForm.LogAppend("MG Damage after change: " + actualHPEffect);
                    actualMPEffect = -damageEaten;

                    healSkillId = skillId;
                }

                if (chr.PrimaryStats.BuffPowerGuard.HasReferenceId(Constants.Fighter.Skills.PowerGuard) ||
                    chr.PrimaryStats.BuffPowerGuard.HasReferenceId(Constants.Page.Skills.PowerGuard))
                {
                    var  skillId = chr.PrimaryStats.BuffPowerGuard.R;
                    byte skillLevel;
                    var  sld = chr.Skills.GetSkillLevelData(skillId, out skillLevel);

                    int damageReflectedBack = (int)(damage * (sld.XValue / 100.0d));

                    if (damageReflectedBack > mob.MaxHP)
                    {
                        damageReflectedBack = (int)(mob.MaxHP * 0.1);
                    }

                    if (mob.IsBoss)
                    {
                        damageReflectedBack /= 2;
                    }

                    mob.GiveDamage(chr, damageReflectedBack);
                    MobPacket.SendMobDamageOrHeal(chr, mobId, damageReflectedBack, false, false);

                    mob.CheckDead(mob.Position);

                    Trace.WriteLine($"Reducing damage by PG. Reflected {damageReflectedBack}");
                    actualHPEffect += damageReflectedBack; // Buff 'damaged' hp, so its less
                    healSkillId     = skillId;
                }


                if (chr.PrimaryStats.BuffMesoGuard.IsSet())
                {
                    var skillId = Constants.ChiefBandit.Skills.MesoGuard;
                    var sld     = chr.Skills.GetSkillLevelData(
                        skillId,
                        out var skillLevel
                        );

                    if (sld != null)
                    {
                        var percentage = sld.XValue;

                        var damageReduction = reducedDamage / 2;
                        var mesoLoss        = damageReduction * percentage / 100;
                        if (damageReduction != 0)
                        {
                            var playerMesos    = chr.Inventory.Mesos;
                            var maxMesosUsable = Math.Min(chr.PrimaryStats.BuffMesoGuard.MesosLeft, playerMesos);
                            if (mesoLoss > maxMesosUsable)
                            {
                                // New calculation. in our version it should actually 'save' the
                                // mesos for a bit.
                                damageReduction = 100 * maxMesosUsable / percentage;
                                mesoLoss        = maxMesosUsable;
                            }

                            if (mesoLoss > 0)
                            {
                                chr.PrimaryStats.BuffMesoGuard.MesosLeft -= mesoLoss;
                                MesosTransfer.PlayerUsedSkill(chr.ID, mesoLoss, skillId);

                                chr.AddMesos(-(mesoLoss), false);

                                Trace.WriteLine($"Reducing damage by mesos. Mesos: {mesoLoss}, maxMesos {maxMesosUsable}, reduction {damageReduction}");
                                actualHPEffect += damageReduction;
                                reducedDamage  -= reducedDamage;
                            }

                            if (chr.PrimaryStats.BuffMesoGuard.MesosLeft <= 0)
                            {
                                // Debuff when out of mesos
                                chr.PrimaryStats.RemoveByReference(skillId);
                            }
                        }
                    }
                }

                SendCharacterDamageByMob(
                    chr,
                    attack,
                    damage,
                    reducedDamage,
                    healSkillId,
                    mobMapId,
                    mobId,
                    stance,
                    isReflected,
                    reflectHitAction,
                    reflectX,
                    reflectY
                    );
            }

            Trace.WriteLine($"Showing damage: {reducedDamage}, {damage}");
            Trace.WriteLine($"Applying damage: HP {actualHPEffect}, MP: {actualMPEffect}");

            if (actualHPEffect < 0)
            {
                chr.ModifyHP((short)actualHPEffect);
            }
            if (actualMPEffect < 0)
            {
                chr.ModifyMP((short)actualMPEffect);
            }

            if (mobSkillLevel != 0 && mobSkillId != 0)
            {
                // Check if the skill exists and has any extra effect.

                if (!DataProvider.MobSkills.TryGetValue(mobSkillId, out var skillLevels))
                {
                    return;
                }

                // Still going strong
                if (!skillLevels.TryGetValue(mobSkillLevel, out var msld))
                {
                    return;
                }
                OnStatChangeByMobSkill(chr, msld);
            }
            else if (mob != null)
            {
                // CUser::OnStatChangeByMobAttack
                if (mob.Data.Attacks == null ||
                    !mob.Data.Attacks.TryGetValue((byte)attack, out var mad))
                {
                    return;
                }
                // Okay, we've got an attack...
                if (mad.Disease <= 0)
                {
                    return;
                }

                // Shit's poisonous!
                // Hmm... We could actually make snails give buffs... hurr

                if (!DataProvider.MobSkills.TryGetValue(mad.Disease, out var skillLevels))
                {
                    return;
                }

                // Still going strong
                if (!skillLevels.TryGetValue(mad.SkillLevel, out var msld))
                {
                    return;
                }
                OnStatChangeByMobSkill(chr, msld);
            }
        }