public static void OnStatChangeByMobSkill(Character chr, MobSkillLevelData msld, short delay = 0) { // See if we can actually set the effect... int prop = 100; if (msld.Prop != 0) { prop = msld.Prop; } if (Rand32.Next() % 100 >= prop) { return; // Luck. } BuffStat setStat = null; int rValue = msld.SkillID | (msld.Level << 16); var ps = chr.PrimaryStats; int nValue = 1; switch ((Constants.MobSkills.Skills)msld.SkillID) { case Constants.MobSkills.Skills.Seal: setStat = ps.BuffSeal; break; case Constants.MobSkills.Skills.Darkness: setStat = ps.BuffDarkness; break; case Constants.MobSkills.Skills.Weakness: setStat = ps.BuffWeakness; break; case Constants.MobSkills.Skills.Stun: setStat = ps.BuffStun; break; case Constants.MobSkills.Skills.Curse: setStat = ps.BuffCurse; break; case Constants.MobSkills.Skills.Poison: setStat = ps.BuffPoison; nValue = msld.X; break; } if (setStat != null && !setStat.IsSet()) { var buffTime = msld.Time * 1000; var stat = setStat.Set(rValue, (short)nValue, BuffStat.GetTimeForBuff(buffTime + delay)); if (stat != 0) { chr.Buffs.FinalizeBuff(stat, delay); } } }
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 void AddItemBuff(int itemid) { var data = DataProvider.Items[itemid]; long buffTime = data.BuffTime; var expireTime = BuffStat.GetTimeForBuff(buffTime); var ps = Character.PrimaryStats; var value = -itemid; BuffValueTypes added = 0; if (data.Accuracy > 0) { added |= ps.BuffAccurancy.Set(value, data.Accuracy, expireTime); } if (data.Avoidance > 0) { added |= ps.BuffAvoidability.Set(value, data.Avoidance, expireTime); } if (data.Speed > 0) { added |= ps.BuffSpeed.Set(value, data.Speed, expireTime); } if (data.MagicAttack > 0) { added |= ps.BuffMagicAttack.Set(value, data.MagicAttack, expireTime); } if (data.WeaponAttack > 0) { added |= ps.BuffWeaponAttack.Set(value, data.WeaponAttack, expireTime); } if (data.WeaponDefense > 0) { added |= ps.BuffWeaponDefense.Set(value, data.WeaponDefense, expireTime); } if (data.Thaw > 0) { added |= ps.BuffThaw.Set(value, data.Thaw, expireTime); } if (added != 0) { FinalizeBuff(added, 0); } BuffValueTypes removed = 0; if (data.Cures.HasFlag(ItemData.CureFlags.Weakness)) { removed |= ps.BuffWeakness.Reset(); } if (data.Cures.HasFlag(ItemData.CureFlags.Poison)) { removed |= ps.BuffPoison.Reset(); } if (data.Cures.HasFlag(ItemData.CureFlags.Curse)) { removed |= ps.BuffCurse.Reset(); } if (data.Cures.HasFlag(ItemData.CureFlags.Darkness)) { removed |= ps.BuffDarkness.Reset(); } if (data.Cures.HasFlag(ItemData.CureFlags.Seal)) { removed |= ps.BuffSeal.Reset(); } FinalizeDebuff(removed); }
public void AddBuff(int SkillID, byte level, short delay = 0) { if (!BuffDataProvider.SkillBuffValues.TryGetValue(SkillID, out var flags)) { return; } if (level == 0xFF) { level = Character.Skills.Skills[SkillID]; } var data = DataProvider.Skills[SkillID].Levels[level]; long time = data.BuffTime * 1000; time += delay; // Fix for MesoGuard expiring... hurr if (SkillID == Constants.ChiefBandit.Skills.MesoGuard) { time += 1000 * 1000; } Trace.WriteLine($"Adding buff from skill {SkillID} lvl {level}: {time}. Flags {flags}"); var expireTime = BuffStat.GetTimeForBuff(time); var ps = Character.PrimaryStats; BuffValueTypes added = 0; if (flags.HasFlag(BuffValueTypes.WeaponAttack)) { added |= ps.BuffWeaponAttack.Set(SkillID, data.WeaponAttack, expireTime); } if (flags.HasFlag(BuffValueTypes.WeaponDefense)) { added |= ps.BuffWeaponDefense.Set(SkillID, data.WeaponDefense, expireTime); } if (flags.HasFlag(BuffValueTypes.MagicAttack)) { added |= ps.BuffMagicAttack.Set(SkillID, data.MagicAttack, expireTime); } if (flags.HasFlag(BuffValueTypes.MagicDefense)) { added |= ps.BuffMagicDefense.Set(SkillID, data.MagicDefense, expireTime); } if (flags.HasFlag(BuffValueTypes.Accurancy)) { added |= ps.BuffAccurancy.Set(SkillID, data.Accurancy, expireTime); } if (flags.HasFlag(BuffValueTypes.Avoidability)) { added |= ps.BuffAvoidability.Set(SkillID, data.Avoidability, expireTime); } //if (flags.Contains(BuffValueTypes.Hands)) added |= ps.BuffHands.Set(SkillID, data.Hands, expireTime); if (flags.HasFlag(BuffValueTypes.Speed)) { added |= ps.BuffSpeed.Set(SkillID, data.Speed, expireTime); } if (flags.HasFlag(BuffValueTypes.Jump)) { added |= ps.BuffJump.Set(SkillID, data.Jump, expireTime); } if (flags.HasFlag(BuffValueTypes.MagicGuard)) { added |= ps.BuffMagicGuard.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.DarkSight)) { added |= ps.BuffDarkSight.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.Booster)) { added |= ps.BuffBooster.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.PowerGuard)) { added |= ps.BuffPowerGuard.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.MaxHP)) { added |= ps.BuffMaxHP.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.MaxMP)) { added |= ps.BuffMaxMP.Set(SkillID, data.YValue, expireTime); } if (flags.HasFlag(BuffValueTypes.Invincible)) { added |= ps.BuffInvincible.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.SoulArrow)) { added |= ps.BuffSoulArrow.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.ComboAttack)) { added |= ps.BuffComboAttack.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.Charges)) { added |= ps.BuffCharges.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.DragonBlood)) { added |= ps.BuffDragonBlood.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.HolySymbol)) { added |= ps.BuffHolySymbol.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.MesoUP)) { added |= ps.BuffMesoUP.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.ShadowPartner)) { added |= ps.BuffShadowPartner.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.PickPocketMesoUP)) { added |= ps.BuffPickPocketMesoUP.Set(SkillID, data.XValue, expireTime); } if (flags.HasFlag(BuffValueTypes.MesoGuard)) { added |= ps.BuffMesoGuard.Set(SkillID, data.XValue, expireTime); ps.BuffMesoGuard.MesosLeft = data.MesosUsage; } FinalizeBuff(added, delay); }