예제 #1
0
 public void endControl()
 {
     if (Controller != null && Controller.Map == MapID)
     {
         MobPacket.SendMobRequestEndControl(Controller, this);
     }
 }
예제 #2
0
        public int KillAllMobs(int mapid, bool damage)
        {
            int amount = 0;

            try
            {
                List <Mob> mobsBackup = new List <Mob>(Mobs);

                lock (mobsBackup)
                {
                    foreach (Mob mob in mobsBackup)
                    {
                        if (mob != null)
                        {
                            if (damage)
                            {
                                MobPacket.SendMobDamageOrHeal(mapid, mob.SpawnID, mob.HP, false);
                            }
                            mob.HP = 0;
                            mob.CheckDead();
                            amount++;
                        }
                    }
                }
                mobsBackup.Clear();
                mobsBackup = null;
            }
            catch { }
            return(amount);
        }
예제 #3
0
        public void Update(long currentTime)
        {
            MobStatValue endFlag = 0;

            BuffPhysicalDamage.TryReset(currentTime, ref endFlag);
            BuffPhysicalDefense.TryReset(currentTime, ref endFlag);
            BuffMagicDamage.TryReset(currentTime, ref endFlag);
            BuffMagicDefense.TryReset(currentTime, ref endFlag);
            BuffAccurrency.TryReset(currentTime, ref endFlag);
            BuffEvasion.TryReset(currentTime, ref endFlag);
            BuffSpeed.TryReset(currentTime, ref endFlag);
            BuffStun.TryReset(currentTime, ref endFlag);
            BuffFreeze.TryReset(currentTime, ref endFlag);
            BuffPoison.TryReset(currentTime, ref endFlag);
            BuffSeal.TryReset(currentTime, ref endFlag);
            BuffDarkness.TryReset(currentTime, ref endFlag);
            BuffPowerUp.TryReset(currentTime, ref endFlag);
            BuffMagicUp.TryReset(currentTime, ref endFlag);
            BuffPowerGuardUp.TryReset(currentTime, ref endFlag);
            BuffMagicGuardUp.TryReset(currentTime, ref endFlag);
            BuffPhysicalImmune.TryReset(currentTime, ref endFlag);
            BuffMagicImmune.TryReset(currentTime, ref endFlag);
            BuffDoom.TryReset(currentTime, ref endFlag);
            BuffWeb.TryReset(currentTime, ref endFlag);
            BuffHardSkin.TryReset(currentTime, ref endFlag);
            BuffAmbush.TryReset(currentTime, ref endFlag);
            BuffVenom.TryReset(currentTime, ref endFlag);
            BuffBlind.TryReset(currentTime, ref endFlag);
            BuffSealSkill.TryReset(currentTime, ref endFlag);

            if (endFlag > 0)
            {
                MobPacket.SendMobStatsTempReset(Mob, endFlag);
            }
        }
예제 #4
0
 public void setControlStatus(MobControlStatus mcs)
 {
     MobPacket.SendMobRequestEndControl(null, this);
     MobPacket.SendMobSpawn(null, this, 0, null, false, false);
     ControlStatus = mcs;
     DataProvider.Maps[MapID].UpdateMobControl(this, false, null);
 }
예제 #5
0
        public void InitAndSpawn()
        {
            Init();

            MobPacket.SendMobSpawn(null, this, 0, null, true, false);

            DataProvider.Maps[MapID].UpdateMobControl(this, true, null);
        }
예제 #6
0
        public static void HandleSummonAttack(Character chr, Packet packet)
        {
            if (!ParseAttackData(chr, packet, out AttackData ad, AttackTypes.Summon))
            {
                return;
            }

            var summonId = ad.SummonID;

            if (chr.Summons.GetSummon(summonId, out var summon))
            {
                //SendMagicAttack(chr, ad);
                SendSummonAttack(chr, summon, ad);
                var totalDamage = 0;
                foreach (var ai in ad.Attacks)
                {
                    try
                    {
                        var mob = chr.Field.GetMob(ai.MobMapId);

                        if (mob != null)
                        {
                            foreach (int amount in ai.Damages)
                            {
                                totalDamage += amount;
                                mob.GiveDamage(chr, amount);
                            }

                            var dead = mob.CheckDead(mob.Position, ai.HitDelay, chr.PrimaryStats.BuffMesoUP.N);

                            if (!dead)
                            {
                                switch (summon.SkillId)
                                {
                                case Constants.Ranger.Skills.SilverHawk:
                                case Constants.Sniper.Skills.GoldenEagle:
                                    var sld = CharacterSkills.GetSkillLevelData(summon.SkillId, summon.SkillLevel);
                                    if (!mob.IsBoss && totalDamage > 0 && Rand32.NextBetween(0, 100) < sld.Property)
                                    {
                                        int  stunTime   = ai.HitDelay + 4000;
                                        long expireTime = MasterThread.CurrentTime + stunTime;
                                        var  stat       = mob.Status.BuffStun.Set(summon.SkillId, -1, expireTime);
                                        MobPacket.SendMobStatsTempSet(mob, ai.HitDelay, stat);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Program.MainForm.LogAppend(ex.ToString());
                    }
                }
            }
        }
예제 #7
0
        public int spawnMob(int spawnid, Life life)
        {
            int id  = _objectIDs.NextValue();
            Mob mob = new Mob(id, ID, life.ID, new Pos(life.X, life.Y), (short)life.Foothold, MobControlStatus.ControlNormal);

            mob.SetSpawnData(life);
            Mobs.Add(mob);
            MobPacket.SendMobSpawn(null, mob, 0, null, true, false);
            UpdateMobControl(mob, true, null);
            return(id);
        }
예제 #8
0
        public int spawnMobNoRespawn(int mobid, Pos position, short foothold)
        {
            int id  = _objectIDs.NextValue();
            Mob mob = new Mob(id, ID, mobid, position, foothold, MobControlStatus.ControlNormal, false);

            Mobs.Add(mob);

            MobPacket.SendMobSpawn(null, mob, 0, null, true, false);
            UpdateMobControl(mob, true, null);

            return(id);
        }
예제 #9
0
 public void setControl(Character control, bool spawn, Character display)
 {
     Controller = control;
     if (HP == 0)
     {
         return;
     }
     if (control != null)
     {
         MobPacket.SendMobRequestControl(control, this, spawn, null);
     }
     else if (ControlStatus == MobControlStatus.ControlNone)
     {
         MobPacket.SendMobRequestControl(control, this, spawn, display);
     }
 }
예제 #10
0
        public int spawnMob(int mobid, Pos position, short foothold, Mob owner, byte summonEffect)
        {
            int id  = _objectIDs.NextValue();
            Mob mob = new Mob(id, ID, mobid, position, foothold, MobControlStatus.ControlNormal, summonEffect != 0 && owner != null);

            Mobs.Add(mob);
            if (summonEffect != 0)
            {
                mob.Owner = owner;
                if (owner != null)
                {
                    owner.SpawnedMobs.Add(id, mob);
                }
            }
            MobPacket.SendMobSpawn(null, mob, summonEffect, owner, (owner == null), false);

            UpdateMobControl(mob, true, null);
            return(id);
        }
예제 #11
0
        public static void HandleRangedAttack(Character chr, Packet packet)
        {
            //Program.MainForm.LogAppend("Handling Ranged");
            if (!ParseAttackData(chr, packet, out AttackData ad, AttackTypes.Ranged))
            {
                return;
            }

            int    TotalDamage;
            bool   died;
            double MaxPossibleDamage = 0;
            Mob    mob;

            SendRangedAttack(chr, ad);

            if (ad.SkillID != 0 || ad.StarID != 0)
            {
                chr.Skills.UseRangedAttack(ad.SkillID, ad.StarItemSlot);
            }

            foreach (var ai in ad.Attacks)
            {
                try
                {
                    TotalDamage = 0;
                    mob         = chr.Field.GetMob(ai.MobMapId);

                    if (mob != null)
                    {
                        foreach (int amount in ai.Damages)
                        {
                            mob.GiveDamage(chr, amount);
                            TotalDamage += amount;
                        }

                        if (TotalDamage != 0)
                        {
                            died = mob.CheckDead(ai.HitPosition, ai.HitDelay, chr.PrimaryStats.BuffMesoUP.N);


                            var sld = chr.Skills.GetSkillLevelData(ad.SkillID, out byte derp);
                            if (sld != null && derp == ad.SkillLevel)
                            {
                                long buffTime = sld.BuffTime * 1000;

                                if (!died)
                                {
                                    if (ad.SkillID == Constants.Hunter.Skills.ArrowBomb && !mob.IsBoss)
                                    {
                                        short chance = RNG.Range.generate((short)0, (short)100);

                                        if (chance < sld.Property)
                                        {
                                            var stat = mob.Status.BuffStun.Set(ad.SkillID, 1,
                                                                               MasterThread.CurrentTime + buffTime);
                                            MobPacket.SendMobStatsTempSet(mob, ai.HitDelay, stat);
                                        }
                                    }
                                }

                                if (ad.SkillID == Constants.Assassin.Skills.Drain)
                                {
                                    double hp = Math.Min(ai.Damages[0] * sld.XValue * 0.01, mob.MaxHP);
                                    hp = Math.Min(hp, chr.PrimaryStats.MaxHP / 2);
                                    chr.ModifyHP((short)(hp));
                                }

                                switch (ad.SkillID)
                                {
                                case Constants.Hunter.Skills.PowerKnockback:
                                case Constants.Crossbowman.Skills.PowerKnockback:
                                    MaxPossibleDamage = DamageFormula.MaximumPowerKnockbackDamage(chr, mob, TotalDamage);
                                    break;

                                case Constants.Hunter.Skills.ArrowBomb:
                                    MaxPossibleDamage =
                                        DamageFormula.MaximumArrowBombDamage(chr, mob, ad.StarID, TotalDamage);
                                    break;

                                case Constants.Rogue.Skills.LuckySeven:
                                    MaxPossibleDamage = DamageFormula.MaximumLuckySevenDamage(chr, mob, TotalDamage);
                                    break;

                                case Constants.Assassin.Skills.Drain:
                                    MaxPossibleDamage = 99999;     //Hotfix a bug that caused legit players to get autobanned. TODO check this properly!
                                    break;

                                default:
                                    MaxPossibleDamage = DamageFormula.MaximumRangedDamage(chr, mob, ad.SkillID, ad.StarID,
                                                                                          ad.Targets, TotalDamage);
                                    break;
                                }

                                if (TotalDamage > MaxPossibleDamage)
                                {
                                    if (ReportDamagehack(chr, ad, TotalDamage, (int)MaxPossibleDamage))
                                    {
                                        return;
                                    }
                                }
                            }
                        }
                    }
                }

                catch (Exception ex)
                {
                    //Program.MainForm.LogAppendFormat(ex.ToString());
                }
            }
        }
예제 #12
0
        public static void HandleMeleeAttack(Character chr, Packet packet)
        {
            //Program.MainForm.LogAppend("Handling Melee");
            if (!ParseAttackData(chr, packet, out AttackData ad, AttackTypes.Melee))
            {
                return;
            }

            SendMeleeAttack(chr, ad);
            Mob    mob;
            bool   died;
            int    TotalDamage = 0;
            double MaxPossibleDamage;

            if (ad.SkillID != 0)
            {
                chr.Skills.UseMeleeAttack(ad.SkillID, ad);
            }

            bool pickPocketActivated = chr.PrimaryStats.HasBuff(Constants.ChiefBandit.Skills.Pickpocket);
            var  pickPocketSLD       = chr.Skills.GetSkillLevelData(Constants.ChiefBandit.Skills.Pickpocket, out byte pickPocketSkillLevel);
            bool pickOk = !ad.IsMesoExplosion && pickPocketActivated && pickPocketSkillLevel > 0 && pickPocketSLD != null;

            int StolenMP       = 0;
            int MpStealSkillID = chr.Skills.GetMpStealSkillData(2, out int MpStealProp, out int MpStealPercent, out byte MpStealLevel);

            List <Drop> dropsToPop = null;
            short       delayForMesoExplosionKill = 0;

            if (ad.SkillID == Constants.ChiefBandit.Skills.MesoExplosion)
            {
                byte items = packet.ReadByte();
                dropsToPop = new List <Drop>(items);
                byte i;
                for (i = 0; i < items; i++)
                {
                    int objectID = packet.ReadInt();
                    packet.Skip(1);

                    if (chr.Field.DropPool.Drops.TryGetValue(objectID, out var drop) &&
                        drop.Reward.Mesos)
                    {
                        dropsToPop.Add(drop);
                    }
                }

                delayForMesoExplosionKill = packet.ReadShort();
            }


            var  sld            = ad.SkillID == 0 ? null : DataProvider.Skills[ad.SkillID].Levels[ad.SkillLevel];
            long buffTime       = sld?.BuffTime * 1000 ?? 0;
            long buffExpireTime = MasterThread.CurrentTime + buffTime;

            bool IsSuccessRoll() => sld != null && (Rand32.Next() % 100) < sld.Property;


            foreach (var ai in ad.Attacks)
            {
                try
                {
                    TotalDamage = 0;
                    mob         = chr.Field.GetMob(ai.MobMapId);

                    if (mob == null)
                    {
                        continue;
                    }

                    bool boss = mob.Data.Boss;

                    if (MpStealPercent > 0)
                    {
                        StolenMP += mob.OnMobMPSteal(MpStealProp, MpStealPercent / ad.Targets);
                    }
                    if (pickOk)
                    {
                        mob.GiveMoney(chr, ai, ad.Hits);
                    }

                    foreach (var amount in ai.Damages)
                    {
                        mob.GiveDamage(chr, amount);
                        TotalDamage += amount;
                    }

                    if (TotalDamage == 0)
                    {
                        continue;
                    }

                    var maxDamage = 5 + (chr.Level * 6);
                    if (ad.SkillID == 0 && chr.Level < 10 && TotalDamage > maxDamage)
                    {
                        chr.PermaBan("Melee damage hack (low level), hit " + TotalDamage + " (max: " + maxDamage + ")");
                        return;
                    }

                    died = mob.CheckDead(ai.HitPosition, ad.IsMesoExplosion ? delayForMesoExplosionKill : ai.HitDelay, chr.PrimaryStats.BuffMesoUP.N);

                    //TODO sometimes when attacking without using a skill this gets triggered and throws a exception?
                    if (died || ad.SkillID <= 0)
                    {
                        continue;
                    }

                    if (ad.SkillID != 0)
                    {
                        MobStatus.MobStatValue addedStats = 0;

                        switch (ad.SkillID)
                        {
                        case Constants.DragonKnight.Skills.Sacrifice:
                        {
                            double percentSacrificed = sld.XValue / 100.0;
                            short  amountSacrificed  = (short)(TotalDamage * percentSacrificed);
                            chr.DamageHP(amountSacrificed);
                            break;
                        }

                        case Constants.Bandit.Skills.Steal:
                            if (!boss && IsSuccessRoll())
                            {
                                mob.GiveReward(chr.ID, 0, DropType.Normal, ai.HitPosition, ai.HitDelay, 0, true);
                            }
                            break;

                        // Debuffs

                        case Constants.Rogue.Skills.Disorder:

                            addedStats  = mob.Status.BuffPhysicalDamage.Set(ad.SkillID, (short)sld.XValue, buffExpireTime);
                            addedStats |= mob.Status.BuffPhysicalDefense.Set(ad.SkillID, (short)sld.XValue, buffExpireTime);
                            break;

                        case Constants.WhiteKnight.Skills.ChargeBlow:     // Not sure if this should add the stun
                        case Constants.Crusader.Skills.AxeComa:
                        case Constants.Crusader.Skills.SwordComa:
                        case Constants.Crusader.Skills.Shout:
                            if (!boss && IsSuccessRoll())
                            {
                                addedStats = mob.Status.BuffStun.Set(ad.SkillID, (short)-sld.BuffTime, buffExpireTime);
                            }
                            //is charge blow supposed to end the elemental charge buff?
                            break;

                        case Constants.Crusader.Skills.AxePanic:
                        case Constants.Crusader.Skills.SwordPanic:
                            if (!boss && IsSuccessRoll())
                            {
                                addedStats = mob.Status.BuffDarkness.Set(ad.SkillID, (short)1, buffExpireTime);
                                //darkness animation doesnt show in this ver?
                            }
                            break;
                        }

                        if (addedStats != 0)
                        {
                            MobPacket.SendMobStatsTempSet(mob, ai.HitDelay, addedStats);
                        }
                    }


                    if (StolenMP > 0)
                    {
                        chr.ModifyMP((short)StolenMP);
                        MapPacket.SendPlayerSkillAnimSelf(chr, MpStealSkillID, MpStealLevel);
                        MapPacket.SendPlayerSkillAnim(chr, MpStealSkillID, MpStealLevel);
                    }

                    if (ad.SkillID != 0)
                    {
                        MaxPossibleDamage = DamageFormula.MaximumMeleeDamage(chr, mob, ad.Targets, ad.SkillID);
                    }
                    else
                    {
                        MaxPossibleDamage = DamageFormula.MaximumMeleeDamage(chr, mob, ad.Targets);
                    }

                    if (TotalDamage > MaxPossibleDamage)
                    {
                        if (ReportDamagehack(chr, ad, TotalDamage, (int)MaxPossibleDamage))
                        {
                            return;
                        }
                    }
                }

                catch (Exception ex)
                {
                    Program.MainForm.LogAppend(ex.ToString());
                }
            }

            if (chr.PrimaryStats.BuffComboAttack.IsSet() && TotalDamage > 0)
            {
                if (ad.SkillID == Constants.Crusader.Skills.AxeComa ||
                    ad.SkillID == Constants.Crusader.Skills.SwordComa ||
                    ad.SkillID == Constants.Crusader.Skills.AxePanic ||
                    ad.SkillID == Constants.Crusader.Skills.SwordPanic)
                {
                    chr.PrimaryStats.BuffComboAttack.N = 1;
                    BuffPacket.SetTempStats(chr, BuffValueTypes.ComboAttack);
                    MapPacket.SendPlayerBuffed(chr, BuffValueTypes.ComboAttack);
                }
                else if (ad.SkillID != Constants.Crusader.Skills.Shout)
                {
                    if (chr.PrimaryStats.BuffComboAttack.N <= chr.PrimaryStats.BuffComboAttack.MaxOrbs)
                    {
                        chr.PrimaryStats.BuffComboAttack.N++;
                        BuffPacket.SetTempStats(chr, BuffValueTypes.ComboAttack);
                        MapPacket.SendPlayerBuffed(chr, BuffValueTypes.ComboAttack);
                    }
                }
            }


            switch (ad.SkillID)
            {
            case 0:                                                                                           // Normal wep
            {
                if (chr.Inventory.GetEquippedItemId((short)Constants.EquipSlots.Slots.Helm, true) == 1002258) // Blue Diamondy Bandana
                {
                    var mobs = chr.Field.GetMobsInRange(chr.Position, new Pos(-10000, -10000), new Pos(10000, 10000));

                    foreach (var m in mobs)
                    {
                        MobPacket.SendMobDamageOrHeal(chr.Field, m, 1337, false, false);

                        if (m.GiveDamage(chr, 1337))
                        {
                            m.CheckDead();
                        }
                    }
                }
                break;
            }

            case Constants.ChiefBandit.Skills.MesoExplosion:
            {
                byte i = 0;
                foreach (var drop in dropsToPop)
                {
                    var delay = (short)Math.Min(1000, delayForMesoExplosionKill + (100 * (i % 5)));
                    chr.Field.DropPool.RemoveDrop(drop, RewardLeaveType.Explode, delay);
                    i++;
                }
                break;
            }

            case Constants.WhiteKnight.Skills.ChargeBlow:
                if (IsSuccessRoll())
                {
                    // RIP. It cancels your charge
                    var removedBuffs = chr.PrimaryStats.RemoveByReference(chr.PrimaryStats.BuffCharges.R);
                    BuffPacket.SetTempStats(chr, removedBuffs);
                    MapPacket.SendPlayerBuffed(chr, removedBuffs);
                }
                break;

            case Constants.WhiteKnight.Skills.BwFireCharge:
            case Constants.WhiteKnight.Skills.BwIceCharge:
            case Constants.WhiteKnight.Skills.BwLitCharge:
            case Constants.WhiteKnight.Skills.SwordFireCharge:
            case Constants.WhiteKnight.Skills.SwordIceCharge:
            case Constants.WhiteKnight.Skills.SwordLitCharge:
            {
                var buff = chr.PrimaryStats.BuffCharges.Set(
                    ad.SkillID,
                    sld.XValue,
                    BuffStat.GetTimeForBuff(1000 * sld.BuffTime)
                    );
                BuffPacket.SetTempStats(chr, buff);
                MapPacket.SendPlayerBuffed(chr, buff);
                break;
            }

            case Constants.DragonKnight.Skills.DragonRoar:
            {
                // Apply stun
                var buff = chr.PrimaryStats.BuffStun.Set(
                    ad.SkillID,
                    1,
                    BuffStat.GetTimeForBuff(1000 * sld.YValue)
                    );
                BuffPacket.SetTempStats(chr, buff);
                MapPacket.SendPlayerBuffed(chr, buff);
                break;
            }
            }
        }
예제 #13
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);
            }
        }
예제 #14
0
        public override void AC_OnPacketInbound(Packet packet)
        {
            ClientMessages header = 0;

            try
            {
                header = (ClientMessages)packet.ReadByte();

                if (!Loaded || Player?.Character == null)
                {
                    switch (header)
                    {
                    case ClientMessages.MIGRATE_IN:
                        OnPlayerLoad(packet);
                        break;     //updated
                    }
                }
                // Block packets as we are migrating
                else if (Server.Instance.InMigration == false || Server.Instance.IsNewServerInMigration)
                {
                    var character = Player.Character;

                    if (logPackets.Contains(header))
                    {
                        PacketLog.ReceivedPacket(packet, (byte)header, Server.Instance.Name, IP);
                    }

                    switch (header)
                    {
                    case ClientMessages.ENTER_PORTAL:
                        MapPacket.OnEnterPortal(packet, character);
                        break;

                    case ClientMessages.CHANGE_CHANNEL:
                        OnChangeChannel(character, packet);
                        break;

                    case ClientMessages.ENTER_CASH_SHOP:
                        OnEnterCashShop(character);
                        break;

                    case ClientMessages.MOVE_PLAYER:
                        MapPacket.HandleMove(character, packet);
                        break;

                    case ClientMessages.SIT_REQUEST:
                        MapPacket.HandleSitChair(character, packet);
                        break;

                    case ClientMessages.ENTER_TOWN_PORTAL:
                        MapPacket.HandleDoorUse(character, packet);
                        break;

                    case ClientMessages.CLOSE_RANGE_ATTACK:
                        AttackPacket.HandleMeleeAttack(character, packet);
                        break;

                    case ClientMessages.RANGED_ATTACK:
                        AttackPacket.HandleRangedAttack(character, packet);
                        break;

                    case ClientMessages.MAGIC_ATTACK:
                        AttackPacket.HandleMagicAttack(character, packet);
                        break;

                    case ClientMessages.TAKE_DAMAGE:
                        CharacterStatsPacket.HandleCharacterDamage(character, packet);
                        break;

                    case ClientMessages.CHAT:
                        MessagePacket.HandleChat(character, packet);
                        break;

                    case ClientMessages.GROUP_MESSAGE:
                        MessagePacket.HandleSpecialChat(character, packet);
                        break;

                    case ClientMessages.WHISPER:
                        MessagePacket.HandleCommand(character, packet);
                        break;

                    case ClientMessages.EMOTE:
                        MapPacket.SendEmotion(character, packet.ReadInt());
                        break;

                    case ClientMessages.NPC_TALK:
                        MapPacket.HandleNPCChat(character, packet);
                        break;

                    case ClientMessages.NPC_TALK_MORE:
                        NpcPacket.HandleNPCChat(character, packet);
                        break;

                    case ClientMessages.SHOP_ACTION:
                        NpcPacket.HandleNPCShop(character, packet);
                        break;

                    case ClientMessages.STORAGE_ACTION:
                        StoragePacket.HandleStorage(character, packet);
                        break;

                    case ClientMessages.ITEM_MOVE:
                        InventoryPacket.HandleInventoryPacket(character, packet);
                        break;

                    case ClientMessages.ITEM_USE:
                        InventoryPacket.HandleUseItemPacket(character, packet);
                        break;

                    case ClientMessages.SUMMON_BAG_USE:
                        InventoryPacket.HandleUseSummonSack(character, packet);
                        break;

                    case ClientMessages.CASH_ITEM_USE:
                        CashPacket.HandleCashItem(character, packet);
                        break;

                    case ClientMessages.RETURN_SCROLL_USE:
                        InventoryPacket.HandleUseReturnScroll(character, packet);
                        break;

                    case ClientMessages.SCROLL_USE:
                        InventoryPacket.HandleScrollItem(character, packet);
                        break;

                    case ClientMessages.DISTRIBUTE_AP:
                        CharacterStatsPacket.HandleStats(character, packet);
                        break;

                    case ClientMessages.HEAL_OVER_TIME:
                        CharacterStatsPacket.HandleHeal(character, packet);
                        break;

                    case ClientMessages.DISTRIBUTE_SP:
                        SkillPacket.HandleAddSkillLevel(character, packet);
                        break;

                    case ClientMessages.PREPARE_SKILL:
                        SkillPacket.HandlePrepareSkill(character, packet);
                        break;

                    case ClientMessages.GIVE_BUFF:
                        SkillPacket.HandleUseSkill(character, packet);
                        break;

                    case ClientMessages.CANCEL_BUFF:
                        SkillPacket.HandleStopSkill(character, packet);
                        break;

                    case ClientMessages.DROP_MESOS:
                        DropPacket.HandleDropMesos(character, packet.ReadInt());
                        break;

                    case ClientMessages.GIVE_FAME:
                        FamePacket.HandleFame(character, packet);
                        break;

                    case ClientMessages.CHAR_INFO_REQUEST:
                        MapPacket.SendPlayerInfo(character, packet);
                        break;

                    case ClientMessages.SPAWN_PET:
                        PetsPacket.HandleSpawnPet(character, packet.ReadShort());
                        break;

                    case ClientMessages.SUMMON_MOVE:
                        MapPacket.HandleSummonMove(character, packet);
                        break;

                    case ClientMessages.SUMMON_ATTACK:
                        AttackPacket.HandleSummonAttack(character, packet);
                        break;

                    case ClientMessages.SUMMON_DAMAGED:
                        MapPacket.HandleSummonDamage(character, packet);
                        break;

                    case ClientMessages.MOB_MOVE:
                        MobPacket.HandleMobControl(character, packet);
                        break;

                    case ClientMessages.NPC_ANIMATE:
                        MapPacket.HandleNPCAnimation(character, packet);
                        break;

                    case ClientMessages.PET_MOVE: PetsPacket.HandleMovePet(character, packet); break;

                    case ClientMessages.PET_INTERACTION: PetsPacket.HandleInteraction(character, packet); break;

                    case ClientMessages.PET_ACTION: PetsPacket.HandlePetAction(character, packet); break;

                    case ClientMessages.FIELD_CONTIMOVE_STATE:
                        MapPacket.OnContiMoveState(character, packet);
                        break;

                    case ClientMessages.DROP_PICK_UP:
                        DropPacket.HandlePickupDrop(character, packet);
                        break;

                    case ClientMessages.MESSENGER:
                        MessengerHandler.HandleMessenger(character, packet);
                        break;

                    case ClientMessages.MINI_ROOM_OPERATION:
                        MiniRoomPacket.HandlePacket(character, packet);
                        break;

                    case ClientMessages.FRIEND_OPERATION:
                        BuddyHandler.HandleBuddy(character, packet);
                        break;

                    case ClientMessages.PARTY_OPERATION:
                        PartyHandler.HandleParty(character, packet);
                        break;

                    case ClientMessages.DENY_PARTY_REQUEST:
                        PartyHandler.HandleDecline(character, packet);
                        break;

                    case ClientMessages.REACTOR_HIT:
                        ReactorPacket.HandleReactorHit(character, packet);
                        break;

                    case ClientMessages.REPORT_USER:
                        MiscPacket.ReportPlayer(character, packet);
                        break;


                    //this is a garbage opcode that i use when doing janky client packet workarounds. This is where packets go to die.
                    case ClientMessages.JUNK:
                        Program.MainForm.LogDebug("received junk packet");
                        break;

                    // eh.. ignore?
                    // Happens when one of the following buffs are set:
                    // Stun, Poison, Seal, Darkness, Weakness, Curse
                    // Maybe patch out of the client
                    case ClientMessages.CHARACTER_IS_DEBUFFED: break;

                    // TODO: Implement???
                    case ClientMessages.MOB_APPLY_CONTROL: break;

                    case ClientMessages.CLIENT_HASH: break;

                    case ClientMessages.PONG:
                        // Make sure we update the player online thing
                        RedisBackend.Instance.SetPlayerOnline(
                            character.UserID,
                            Server.Instance.GetOnlineId()
                            );

                        // Cleanup expired items
                        character.Inventory.CheckExpired();
                        break;

                    default:
                        if (character.Field.HandlePacket(character, packet, header) == false)
                        {
                            Program.MainForm.LogAppend(
                                "[{0}] Unknown packet received! " + packet,
                                header
                                );
                        }

                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Program.MainForm.LogAppend($"---- ERROR ----\r\n{ex}");
                Program.MainForm.LogAppend($"Packet: {packet}");
                FileWriter.WriteLine(@"etclog\ExceptionCatcher.log", "[Game Server " + Server.Instance.ID + "][" + DateTime.Now + "] Exception caught: " + ex, true);
                //Disconnect();
            }


#if DEBUG
            if (packet.Length != packet.Position)
            {
                var packetStr = packet.ToString();
                packetStr = packetStr.Substring(0, packet.Position * 3 - 1) + "-x-" + packetStr.Substring(packet.Position * 3);

                log.Debug($"Did not read full message in packet: {header} {packetStr}");
            }
#endif
        }
예제 #15
0
        public void CheckDead(Pos killPos = null)
        {
            if (HP == 0 && !IsDead)
            {
                IsDead = true;
                if (killPos != null)
                {
                    Position = killPos;
                }
                setControl(null, false, null);
                MobPacket.SendMobDeath(this, 1);
                Character maxDmgChar   = null;
                ulong     maxDmgAmount = 0;
                MobData   md           = DataProvider.Mobs[MobID];
                DeadsInFiveMinutes++;

                foreach (KeyValuePair <int, ulong> dmg in Damages)
                {
                    if (maxDmgAmount < dmg.Value && Server.Instance.CharacterList.ContainsKey(dmg.Key))
                    {
                        Character chr = Server.Instance.CharacterList[dmg.Key];
                        if (chr.Map == MapID)
                        {
                            maxDmgAmount = dmg.Value;
                            maxDmgChar   = chr;
                        }
                    }
                }

                if (maxDmgChar != null)
                {
                    if (Server.Instance.CharacterDatabase.isDonator(maxDmgChar.UserID) || Map.isPremium(maxDmgChar.Map))
                    {
                        maxDmgChar.AddEXP((double)EXP * Server.Instance.mobExpRate * Map.MAP_PREMIUM_EXP); //Premium Maps, also known as Internet Cafe maps
                    }
                    else
                    {
                        maxDmgChar.AddEXP((uint)EXP * Server.Instance.mobExpRate);
                    }
                    DropPacket.HandleDrops(maxDmgChar, MapID, Constants.getDropName(MobID, true), SpawnID, Position, false, false, false);
                }

                foreach (int mobid in md.Revive)
                {
                    DataProvider.Maps[MapID].spawnMobNoRespawn(mobid, killPos, Foothold);
                }



                Damages.Clear();

                if (DoesRespawn)
                {
                    Position = OriginalPosition;
                    Foothold = OriginalFoothold;
                    int derp = (int)(((((SpawnData == null ? 10 : SpawnData.RespawnTime + 1) / DataProvider.Maps[MapID].MobRate)) * DeadsInFiveMinutes) * 5);

                    MasterThread.MasterThread.Instance.AddRepeatingAction(new MasterThread.MasterThread.RepeatingAction("Mob Remover", (date) =>
                    {
                        InitAndSpawn();
                    }, (ulong)derp * 1000, 0));
                }
                else
                {
                    Clearup();
                    DataProvider.Maps[MapID].RemoveMob(this);
                }
            }
        }
예제 #16
0
        public static void HandleMagicAttack(Character chr, Packet packet)
        {
            //Program.MainForm.LogAppend("Handling Magic");
            if (!ParseAttackData(chr, packet, out AttackData ad, AttackTypes.Magic))
            {
                return;
            }

            int    TotalDamage;
            double MaxSpellDamage;
            bool   died;
            Mob    mob;

            SendMagicAttack(chr, ad);


            if (ad.SkillID != 0)
            {
                chr.Skills.UseMeleeAttack(ad.SkillID, ad);
            }

            int StolenMP       = 0;
            int MpStealSkillID = chr.Skills.GetMpStealSkillData(2, out int MpStealProp, out int MpStealPercent, out byte MpStealLevel);

            foreach (var ai in ad.Attacks)
            {
                try
                {
                    TotalDamage = 0;
                    mob         = chr.Field.GetMob(ai.MobMapId);

                    if (mob == null)
                    {
                        continue;
                    }

                    foreach (int amount in ai.Damages)
                    {
                        mob.GiveDamage(chr, amount);
                        TotalDamage += amount;
                    }

                    if (MpStealPercent > 0)
                    {
                        StolenMP += mob.OnMobMPSteal(MpStealProp, MpStealPercent / ad.Targets);
                    }

                    if (TotalDamage == 0)
                    {
                        continue;
                    }

                    died = mob.CheckDead(ai.HitPosition, ai.HitDelay, chr.PrimaryStats.BuffMesoUP.N);

                    if (!died)
                    {
                        var  sld      = DataProvider.Skills[ad.SkillID].Levels[ad.SkillLevel];
                        long buffTime = sld.BuffTime * 1000;

                        //TODO refactor element code when we get the proper element loading with calcdamage branch
                        if ((sld.ElementFlags == SkillElement.Ice || ad.SkillID == Constants.ILMage.Skills.ElementComposition) && !mob.IsBoss)
                        {
                            var stat = mob.Status.BuffFreeze.Set(ad.SkillID, 1, MasterThread.CurrentTime + buffTime);
                            MobPacket.SendMobStatsTempSet(mob, ai.HitDelay, stat);
                        }

                        if (ad.SkillID == Constants.FPMage.Skills.ElementComposition && !mob.IsBoss)
                        {
                            if (Rand32.NextBetween(0, 100) < sld.Property)
                            {
                                mob.DoPoison(chr.ID, chr.Skills.GetSkillLevel(ad.SkillID), buffTime, ad.SkillID, sld.MagicAttack, ai.HitDelay);
                            }
                        }

                        if (ad.SkillID == Constants.FPWizard.Skills.PoisonBreath)
                        {
                            if (Rand32.NextBetween(0, 100) < sld.Property)
                            {
                                mob.DoPoison(chr.ID, chr.Skills.GetSkillLevel(ad.SkillID), buffTime, ad.SkillID, sld.MagicAttack, ai.HitDelay);
                            }
                        }

                        if (StolenMP > 0)
                        {
                            chr.ModifyMP((short)StolenMP);
                            MapPacket.SendPlayerSkillAnimSelf(chr, MpStealSkillID, MpStealLevel);
                            MapPacket.SendPlayerSkillAnim(chr, MpStealSkillID, MpStealLevel);
                        }
                    }

                    switch (ad.SkillID)
                    {
                    case Constants.Magician.Skills.MagicClaw:     // If the Spell is Magic Claw (since it registers two hits)
                        MaxSpellDamage = DamageFormula.MaximumSpellDamage(chr, mob, ad.SkillID) * 2;
                        break;

                    case Constants.Cleric.Skills.Heal:     // If the Spell is Heal (since it has a special snowflake calculation for it)
                        if (mob.Data.Undead == false)
                        {
                            chr.PermaBan("Heal damaging regular mobs exploit");
                            return;
                        }
                        MaxSpellDamage = DamageFormula.MaximumHealDamage(chr, mob, ad.Targets);
                        break;

                    default:
                        MaxSpellDamage = DamageFormula.MaximumSpellDamage(chr, mob, ad.SkillID);
                        break;
                    }

                    if (TotalDamage > MaxSpellDamage)
                    {
                        if (ReportDamagehack(chr, ad, TotalDamage, (int)MaxSpellDamage))
                        {
                            return;
                        }
                    }
                }

                catch (Exception ex)
                {
                    Program.MainForm.LogAppend(ex.ToString());
                }
            }
        }
예제 #17
0
        public void ShowObjects(Character chr)
        {
            if (HasClock)
            {
                MapPacket.SendMapClock(chr, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
            }

            if (chr.Pets.GetEquippedPet() != null)
            {
                Pet pet = chr.Pets.GetEquippedPet();
                pet.Position = new Pos(chr.Position);
                pet.Foothold = chr.Foothold;
                pet.Stance   = 0;
            }
            chr.Pets.SpawnPet();
            Characters.ForEach(p =>
            {
                if (p != chr)
                {
                    MapPacket.SendCharacterEnterPacket(p, chr);
                    MapPacket.SendCharacterEnterPacket(chr, p);
                    p.Pets.SpawnPet(chr);
                    if (p.Summons.mSummon != null)
                    {
                        SummonPacket.SendShowSummon(p, p.Summons.mSummon, false, chr);
                    }
                    if (p.Summons.mPuppet != null)
                    {
                        SummonPacket.SendShowSummon(p, p.Summons.mPuppet, false, chr);
                    }
                }
            });
            Life[(int)SpawnTypes.Npc].ForEach(n => { MapPacket.ShowNPC(n, chr); });

            Mobs.ForEach(m =>
            {
                if (m.HP != 0)
                {
                    if (m.ControlStatus == MobControlStatus.ControlNone)
                    {
                        UpdateMobControl(m, true, chr);
                    }
                    else
                    {
                        MobPacket.SendMobSpawn(chr, m, 0, null, false, true);
                        UpdateMobControl(m, false, null);
                    }
                }
            });

            foreach (KeyValuePair <int, Drop> drop in Drops)
            {
                drop.Value.ShowDrop(chr);
            }



            if (mWeatherID != 0)
            {
                MapPacket.SendWeatherEffect(ID, chr);
            }
        }
예제 #18
0
        public static void HandleUseSkill(Character chr, Packet packet)
        {
            if (chr.PrimaryStats.HP == 0)
            {
                // We don't like zombies
                InventoryPacket.NoChange(chr);
                return;
            }

            var field = chr.Field;

            var SkillID    = packet.ReadInt();
            var SkillLevel = packet.ReadByte();


            if (SkillID == Constants.Priest.Skills.MysticDoor && MasterThread.CurrentTime - chr.tLastDoor < 3000)
            {
                //hack fix for door dc bug
                InventoryPacket.NoChange(chr);
                return;
            }

            if (!chr.Skills.Skills.ContainsKey(SkillID) ||
                SkillLevel < 1 ||
                SkillLevel > chr.Skills.Skills[SkillID])
            {
                Program.MainForm.LogAppend("Player {0} tried to use a skill without having it.", chr.ID);
                ReportManager.FileNewReport("Player {0} tried to use a skill without having it.", chr.ID, 0);
                chr.Player.Socket.Disconnect();
                return;
            }

            var isGMHideSkill = SkillID == Constants.Gm.Skills.Hide;

            // Prevent sending the hide enable/disable packet to others
            if (isGMHideSkill)
            {
                chr.SetHide(chr.GMHideEnabled == false, false);
                if (chr.GMHideEnabled == false)
                {
                    StopSkill(chr, SkillID);
                }
            }
            else if (SkillID != Constants.Cleric.Skills.Heal ||
                     chr.Inventory.GetEquippedItemId((short)Constants.EquipSlots.Slots.Weapon, false) == 0)
            {
                // If you are using Heal, and are not using a wand/weapon, it won't show anything.

                MapPacket.SendPlayerSkillAnim(chr, SkillID, SkillLevel);
            }

            var sld = DataProvider.Skills[SkillID].Levels[SkillLevel];


            if (SkillID == (int)Constants.Spearman.Skills.HyperBody && !chr.PrimaryStats.HasBuff((int)Constants.Spearman.Skills.HyperBody)) // Buff already exists, do not execute bonus again. Allow multiple casts for duration refresh
            {
                var hpmpBonus = (short)((double)chr.PrimaryStats.MaxHP * ((double)sld.XValue / 100.0d));
                chr.PrimaryStats.BuffBonuses.MaxHP = hpmpBonus;
                hpmpBonus = (short)((double)chr.PrimaryStats.MaxMP * ((double)sld.YValue / 100.0d));
                chr.PrimaryStats.BuffBonuses.MaxMP = hpmpBonus;
            }

            short skillDelay = 0;

            IEnumerable <Character> getCharactersForPartyBuff(byte Flags, bool deadPlayersToo = false)
            {
                if (chr.PartyID == 0)
                {
                    yield break;
                }

                if (PartyData.Parties.TryGetValue(chr.PartyID, out var party))
                {
                    for (var i = 5; i >= 0; i--)
                    {
                        if ((Flags >> (5 - i) & 1) == 0)
                        {
                            continue;
                        }

                        var charid = party.Members[i];
                        if (charid == 0)
                        {
                            continue;
                        }

                        var affected = Server.Instance.GetCharacter(charid);

                        if (affected != null && chr.MapID == affected.MapID &&
                            (deadPlayersToo || affected.PrimaryStats.HP > 0))
                        {
                            yield return(affected);
                        }
                    }
                }
            }

            void handlePartyEffects(byte Flags, short Delay, bool deadPlayersToo = false, Action <Character> additionalEffects = null)
            {
                handlePartyEffectsWithPlayers(getCharactersForPartyBuff(Flags, deadPlayersToo), Delay, additionalEffects);
            }

            void handlePartyEffectsWithPlayers(IEnumerable <Character> characters, short Delay, Action <Character> additionalEffects = null)
            {
                foreach (var character in characters)
                {
                    if (character == chr)
                    {
                        continue;
                    }
                    if (!computerSaysYes())
                    {
                        continue;
                    }

                    MapPacket.SendPlayerSkillAnimThirdParty(character, SkillID, SkillLevel, true, true);
                    MapPacket.SendPlayerSkillAnimThirdParty(character, SkillID, SkillLevel, true, false);
                    additionalEffects?.Invoke(character);
                }
            }

            // Runs func() for each mob inside the packet that:
            // - is not a boss (when isBossable is false)
            // - effect chance was a success
            void handleMobStatEffects(bool isBossable, Action <Mob, short> func)
            {
                var mobCount = packet.ReadByte();
                var mobs     = new List <Mob>(mobCount);

                for (var i = 0; i < mobCount; i++)
                {
                    var mobId = packet.ReadInt();
                    var mob   = field.GetMob(mobId);
                    if (mob == null)
                    {
                        continue;
                    }

                    if (chr.AssertForHack(mob.IsBoss && !isBossable, "Tried hitting boss with non-boss skill", false))
                    {
                        continue;
                    }

                    if (computerSaysYes())
                    {
                        mobs.Add(mob);
                    }
                }

                var delay = packet.ReadShort();

                mobs.ForEach(x => func(x, delay));
            }

            IEnumerable <Character> getFullMapPlayersForGMSkill()
            {
                return(field.Characters.Where(victim =>
                {
                    if (victim == chr)
                    {
                        return false;
                    }
                    if (chr.GMHideEnabled && victim.GMHideEnabled == false)
                    {
                        return false;
                    }

                    // Only Admins can buff other regular people
                    if (chr.IsGM && !chr.IsAdmin && !victim.IsGM)
                    {
                        return false;
                    }

                    return true;
                }));
            }

            bool computerSaysYes()
            {
                var chance = sld.Property;

                if (chance == 0)
                {
                    chance = 100;
                }
                return(!(Rand32.Next() % 100 >= chance));
            }

            //TODO refactor copy-pasted "nearest 6 mobs" logic
            switch (SkillID)
            {
            case Constants.Assassin.Skills.Haste:
            case Constants.Bandit.Skills.Haste:
            case Constants.Cleric.Skills.Bless:
            case Constants.Spearman.Skills.IronWill:
            case Constants.Fighter.Skills.Rage:
            case Constants.FPWizard.Skills.Meditation:
            case Constants.ILWizard.Skills.Meditation:
            case Constants.Hermit.Skills.MesoUp:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });
                break;
            }

            case Constants.Spearman.Skills.HyperBody:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        if (!victim.PrimaryStats.HasBuff((int)Constants.Spearman.Skills.HyperBody))     // Buff already exists, do not execute bonus again. Allow multiple casts for duration refresh
                        {
                            var hpmpBonus = (short)((double)victim.PrimaryStats.MaxHP * ((double)sld.XValue / 100.0d));
                            victim.PrimaryStats.BuffBonuses.MaxHP = hpmpBonus;
                            hpmpBonus = (short)((double)victim.PrimaryStats.MaxMP * ((double)sld.YValue / 100.0d));
                            victim.PrimaryStats.BuffBonuses.MaxMP = hpmpBonus;
                        }
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });


                break;
            }

            case Constants.Cleric.Skills.Heal:
            {
                var Flags   = packet.ReadByte();
                var Delay   = packet.ReadShort();
                var members = getCharactersForPartyBuff(Flags, false);
                var count   = members.Count();

                double healRate = 0;
                if (sld.HPProperty > 0)
                {
                    int chrInt = chr.PrimaryStats.getTotalInt();
                    int chrLuk = chr.PrimaryStats.getTotalLuk();
                    var rate   = (chrInt * 0.8) + Rand32.Next() % (chrInt * 0.2);
                    healRate = (((rate * 1.5 + chrLuk) * (chr.PrimaryStats.getTotalMagicAttack() + chr.PrimaryStats.BuffMagicAttack.N) * 0.01) * (count * 0.3 + 1.0) * sld.HPProperty * 0.01);
                }

                var bigHeal = Math.Min(((long)healRate / (count == 0 ? 1 : count)), short.MaxValue);         //prevent integer overflow caused by high stats. Set count to 1 when not in party
                var heal    = (short)bigHeal;
                chr.ModifyHP((short)Math.Min(heal, (chr.PrimaryStats.GetMaxHP() - chr.PrimaryStats.HP)));

                handlePartyEffectsWithPlayers(members, Delay, victim =>
                    {
                        int oldHP = victim.PrimaryStats.HP;
                        victim.ModifyHP((short)Math.Min(heal, (victim.PrimaryStats.GetMaxHP() - victim.PrimaryStats.HP)));
                        chr.AddEXP(20 * ((victim.PrimaryStats.HP - oldHP) / (8 * victim.Level + 190)), true);
                    });

                break;
            }

            case Constants.Priest.Skills.Dispel:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                if (computerSaysYes())
                {
                    chr.Buffs.Dispell();
                }

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        victim.Buffs.Dispell();
                    });

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobStatus.MobStatValue Flag = 0;
                        Flag |= mob.Status.BuffPowerUp.Reset();
                        Flag |= mob.Status.BuffMagicUp.Reset();
                        Flag |= mob.Status.BuffMagicGuardUp.Reset();
                        Flag |= mob.Status.BuffPowerGuardUp.Reset();
                        Flag |= mob.Status.BuffHardSkin.Reset();
                        MobPacket.SendMobStatsTempReset(mob, Flag);
                    });

                break;
            }

            case Constants.Priest.Skills.HolySymbol:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });

                break;
            }

            // DOOR
            case Constants.Priest.Skills.MysticDoor:
            {
                var x = packet.ReadShort();
                var y = packet.ReadShort();

                if (chr.DoorMapId != Constants.InvalidMap)
                {
                    DataProvider.Maps[chr.DoorMapId].DoorPool.TryRemoveDoor(chr.ID);
                }

                chr.DoorMapId = chr.MapID;
                field.DoorPool.CreateDoor(chr, x, y, MasterThread.CurrentTime + sld.BuffTime * 1000);
                chr.tLastDoor = MasterThread.CurrentTime;
                break;
            }

            // GM SKILLS
            case Constants.Gm.Skills.Haste:
            case Constants.Gm.Skills.HolySymbol:
            case Constants.Gm.Skills.Bless:
            {
                getFullMapPlayersForGMSkill().ForEach(victim =>
                    {
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, true);
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, false);
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });
                break;
            }

            case Constants.Gm.Skills.HealPlusDispell:
            {
                getFullMapPlayersForGMSkill().ForEach(victim =>
                    {
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, true);
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, false);
                        victim.ModifyHP(victim.PrimaryStats.GetMaxMP(false), true);
                        victim.ModifyMP(victim.PrimaryStats.GetMaxMP(false), true);
                        victim.Buffs.Dispell();
                    });
                chr.ModifyHP(chr.PrimaryStats.GetMaxMP(false), true);
                chr.ModifyMP(chr.PrimaryStats.GetMaxMP(false), true);
                chr.Buffs.Dispell();
                break;
            }

            case Constants.Gm.Skills.Resurrection:
            {
                getFullMapPlayersForGMSkill().ForEach(victim =>
                    {
                        if (victim.PrimaryStats.HP <= 0)
                        {
                            MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, true);
                            MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, false);
                            victim.ModifyHP(victim.PrimaryStats.GetMaxHP(false), true);
                        }
                    });
                break;
            }

            // MOB SKILLS
            case Constants.Page.Skills.Threaten:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        var stat = mob.Status.BuffPhysicalDamage.Set(
                            SkillID,
                            (short)-((mob.Data.PAD * SkillLevel) / 100),
                            buffTime + delay
                            );

                        stat |= mob.Status.BuffPhysicalDefense.Set(
                            SkillID,
                            (short)-((mob.Data.PDD * SkillLevel) / 100),
                            buffTime + delay
                            );

                        MobPacket.SendMobStatsTempSet(mob, delay, stat);
                    });
                break;
            }

            case Constants.FPWizard.Skills.Slow:
            case Constants.ILWizard.Skills.Slow:
            {
                var buffNValue = sld.XValue;
                var buffTime   = MasterThread.CurrentTime + (sld.BuffTime * 1000);

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempSet(mob, delay, mob.Status.BuffSpeed.Set(SkillID, buffNValue, buffTime + delay));
                    });
                break;
            }

            case Constants.Gm.Skills.ItemExplosion:
            {
                field.DropPool.Clear(RewardLeaveType.Explode);
                // TODO: Explode people and such
                break;
            }

            case Constants.WhiteKnight.Skills.MagicCrash:
            {
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempReset(mob, mob.Status.BuffMagicGuardUp.Reset());
                    });
                break;
            }

            case Constants.DragonKnight.Skills.PowerCrash:
            {
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempReset(mob, mob.Status.BuffPowerUp.Reset());
                    });
                break;
            }

            case Constants.Crusader.Skills.ArmorCrash:
            {
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempReset(mob, mob.Status.BuffPowerGuardUp.Reset());
                    });
                break;
            }

            case Constants.ILMage.Skills.Seal:
            case Constants.FPMage.Skills.Seal:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempSet(mob, delay, mob.Status.BuffSeal.Set(SkillID, 1, buffTime + delay));
                    });

                break;
            }

            case Constants.Hermit.Skills.ShadowWeb:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        var stat = mob.Status.BuffWeb.Set(
                            SkillID,
                            (short)(mob.MaxHP / (50 - SkillLevel)),
                            buffTime + delay
                            );
                        MobPacket.SendMobStatsTempSet(mob, delay, stat);
                    });
                break;
            }

            case Constants.Priest.Skills.Doom:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempSet(mob, delay, mob.Status.BuffDoom.Set(SkillID, 1, buffTime + delay));
                    });
                break;
            }

            // SUMMONS
            case Constants.Priest.Skills.SummonDragon:
            case Constants.Ranger.Skills.SilverHawk:
            case Constants.Sniper.Skills.GoldenEagle:
            {
                var X = packet.ReadShort();
                var Y = packet.ReadShort();

                var    fh   = field.GetFootholdUnderneath(X, Y, out var MaxY);
                ushort fhid = 0;
                fhid = fh?.ID ?? (ushort)chr.Foothold;
                var bird = new Summon(chr, SkillID, SkillLevel, X, Y, true, fhid, MasterThread.CurrentTime + sld.BuffTime * 1000);
                chr.Summons.SetSummon(bird);

                break;
            }

            case Constants.Ranger.Skills.Puppet:
            case Constants.Sniper.Skills.Puppet:
            {
                Program.MainForm.LogDebug(packet.ToString());
                var    X     = packet.ReadShort();
                var    Y     = packet.ReadShort();
                var    fh    = field.GetFootholdUnderneath(X, Y, out var MaxY);
                var    floor = field.FindFloor(new Pos(X, Y));
                ushort fhid  = 0;
                fhid = fh?.ID ?? (ushort)chr.Foothold;
                var puppet = new Puppet(chr, SkillID, SkillLevel, floor.X, floor.Y, false, fhid, MasterThread.CurrentTime + sld.BuffTime * 1000, sld.XValue);
                chr.Summons.SetSummon(puppet);
                break;
            }
            }

            if (packet.Length == packet.Position + 2)
            {
                // Read the delay...
                skillDelay = packet.ReadShort();
            }

            // Handle regular skill stuff
            if (!isGMHideSkill || chr.GMHideEnabled)
            {
                chr.Buffs.AddBuff(SkillID, SkillLevel, skillDelay);
            }

            InventoryPacket.NoChange(chr);
            chr.Skills.DoSkillCost(SkillID, SkillLevel);

            if (sld.Speed > 0)
            {
                MapPacket.SendAvatarModified(chr, MapPacket.AvatarModFlag.Speed);
            }
        }