public void endControl() { if (Controller != null && Controller.Map == MapID) { MobPacket.SendMobRequestEndControl(Controller, this); } }
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); }
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); } }
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); }
public void InitAndSpawn() { Init(); MobPacket.SendMobSpawn(null, this, 0, null, true, false); DataProvider.Maps[MapID].UpdateMobControl(this, true, null); }
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()); } } } }
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); }
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); }
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); } }
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); }
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()); } } }
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; } } }
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); } }
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 }
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); } } }
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()); } } }
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); } }
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); } }