/// <summary> /// Handles explosion. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="fireballProp"></param> private void Impact(Creature attacker, Skill skill, Prop fireballProp) { var regionId = attacker.RegionId; var propPos = fireballProp.GetPosition(); var targetLocation = new Location(regionId, propPos); var targets = attacker.GetTargetableCreaturesAround(propPos, ExplosionRadius); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetLocation.ToLocationId(), skill.Info.Id); aAction.Set(AttackerOptions.UseEffect); aAction.PropId = fireballProp.EntityId; var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); foreach (var target in targets) { target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result | TargetOptions.KnockDown); tAction.Stun = TargetStun; tAction.Delay = 200; tAction.EffectFlags = EffectFlags.SpecialRangeHit; cap.Add(tAction); // Damage var damage = this.GetDamage(attacker, skill); // Elements damage *= this.GetElementalDamageMultiplier(attacker, target); // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage SkillHelper.HandleMagicDefenseProtection(target, ref damage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref damage, tAction); ManaDeflector.Handle(attacker, target, ref damage, tAction); // Deal damage if (damage > 0) { target.TakeDamage(tAction.Damage = damage, attacker); } target.Aggro(attacker); // Knockback target.Stability = Creature.MinStability; target.GetShoved(fireballProp, KnockbackDistance); if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } this.Train(skill, tAction); } cap.Handle(); }
/// <summary> /// Bolt specific use code. /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="target"></param> protected override void UseSkillOnTarget(Creature attacker, Skill skill, Creature mainTarget) { // Create actions var aAction = new AttackerAction(CombatActionType.RangeHit, attacker, mainTarget.EntityId); aAction.Set(AttackerOptions.Result); var cap = new CombatActionPack(attacker, skill.Info.Id, aAction); // Get targets // Add the main target as first target, so it gets the first hit, // and the full damage. var targets = new List <Creature>(); targets.Add(mainTarget); var inSplashRange = attacker.GetTargetableCreaturesAround(mainTarget.GetPosition(), SplashRange); targets.AddRange(inSplashRange.Where(a => a != mainTarget)); // Damage var damage = this.GetDamage(attacker, skill); var max = Math.Min(targets.Count, skill.Stacks); for (int i = 0; i < max; ++i) { var target = targets[i]; var targetDamage = damage; target.StopMove(); var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, skill.Info.Id); tAction.Set(TargetOptions.Result); tAction.Stun = TargetStun; // Full damage for the first target, -10% for every subsequent one. targetDamage -= (targetDamage * 0.1f) * i; // Critical Hit var critChance = attacker.GetTotalCritChance(target.Protection, true); CriticalHit.Handle(attacker, critChance, ref damage, tAction); // Reduce damage Defense.Handle(aAction, tAction, ref targetDamage); SkillHelper.HandleMagicDefenseProtection(target, ref targetDamage); SkillHelper.HandleConditions(attacker, target, ref damage); ManaShield.Handle(target, ref targetDamage, tAction); // Mana Deflector var mdResult = ManaDeflector.Handle(attacker, target, ref targetDamage, tAction); var delayReduction = mdResult.DelayReduction; var pinged = mdResult.Pinged; // Deal damage if (targetDamage > 0) { target.TakeDamage(tAction.Damage = targetDamage, attacker); } if (target == mainTarget) { target.Aggro(attacker); } // Reduce stun, based on ping if (pinged && delayReduction > 0) { tAction.Stun = (short)Math.Max(0, tAction.Stun - (tAction.Stun / 100 * delayReduction)); } // Death/Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); } else { // If knocked down, instant recovery, // if repeat hit, knock down, // otherwise potential knock back. if (target.IsKnockedDown) { tAction.Stun = 0; } else if (target.Stability < MinStability) { tAction.Set(TargetOptions.KnockDown); } else { // If number of stacks is greater than the number of // targets hit, the targets are knocked back, which is // done by reducing the stability to min here. // Targets with high enough Mana Deflector might // negate this knock back, by reducing the stability // reduction to 0. var stabilityReduction = (skill.Stacks > targets.Count ? OverchargeStabilityReduction : StabilityReduction); // Reduce reduction, based on ping // While the Wiki says that "the Knockdown Gauge [does not] // build up", tests show that it does. However, it's // reduced, assumedly based on the MD rank. if (delayReduction > 0) { stabilityReduction = (short)Math.Max(0, stabilityReduction - (stabilityReduction / 100 * delayReduction)); } target.Stability -= stabilityReduction; if (target.IsUnstable) { tAction.Set(TargetOptions.KnockBack); } } } if (tAction.IsKnockBack) { attacker.Shove(target, KnockbackDistance); } cap.Add(tAction); } // Override stun set by defense aAction.Stun = AttackerStun; Send.Effect(attacker, Effect.UseMagic, EffectSkillName); Send.SkillUseStun(attacker, skill.Info.Id, aAction.Stun, 1); skill.Stacks = 0; // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); }
/// <summary> /// Uses LightningRod /// </summary> /// <param name="attacker"></param> /// <param name="skill"></param> /// <param name="packet"></param> public void Use(Creature attacker, Skill skill, Packet packet) { // Set full charge variable attacker.Temp.LightningRodFullCharge = (DateTime.Now >= attacker.Temp.LightningRodPrepareTime.AddMilliseconds(skill.RankData.Var3)); // Get direction for target Area var direction = Mabi.MabiMath.ByteToRadian(attacker.Direction); var attackerPos = attacker.GetPosition(); // Calculate polygon points var r = MabiMath.ByteToRadian(attacker.Direction); var poe = attackerPos.GetRelative(r, 800); var pivot = new Point(poe.X, poe.Y); var p1 = new Point(pivot.X - SkillLength / 2, pivot.Y - SkillWidth / 2); var p2 = new Point(pivot.X - SkillLength / 2, pivot.Y + SkillWidth / 2); var p3 = new Point(pivot.X + SkillLength / 2, pivot.Y + SkillWidth / 2); var p4 = new Point(pivot.X + SkillLength / 2, pivot.Y - SkillWidth / 2); p1 = this.RotatePoint(p1, pivot, r); p2 = this.RotatePoint(p2, pivot, r); p3 = this.RotatePoint(p3, pivot, r); p4 = this.RotatePoint(p4, pivot, r); // TargetProp var lProp = new Prop(280, attacker.RegionId, poe.X, poe.Y, MabiMath.ByteToRadian(attacker.Direction), 1f, 0f, "single"); attacker.Region.AddProp(lProp); // Prepare Combat Actions var cap = new CombatActionPack(attacker, skill.Info.Id); var targetAreaId = new Location(attacker.RegionId, poe).ToLocationId(); var aAction = new AttackerAction(CombatActionType.SpecialHit, attacker, targetAreaId); aAction.Set(AttackerOptions.KnockBackHit1 | AttackerOptions.UseEffect); aAction.PropId = lProp.EntityId; cap.Add(aAction); // Get targets in Polygon - includes collission check var targets = attacker.Region.GetCreaturesInPolygon(p1, p2, p3, p4).Where(x => attacker.CanTarget(x) && !attacker.Region.Collisions.Any(attacker.GetPosition(), x.GetPosition())).ToList(); var rnd = RandomProvider.Get(); // Check crit var crit = false; var critSkill = attacker.Skills.Get(SkillId.CriticalHit); if (critSkill != null && critSkill.Info.Rank > SkillRank.Novice) { var critChance = Math2.Clamp(0, 30, attacker.GetTotalCritChance(0)); if (rnd.NextDouble() * 100 < critChance) { crit = true; } } foreach (var target in targets) { var tAction = new TargetAction(CombatActionType.TakeHit, target, attacker, SkillId.CombatMastery); tAction.Set(TargetOptions.None); tAction.AttackerSkillId = skill.Info.Id; cap.Add(tAction); var damage = attacker.GetRndMagicDamage(skill, skill.RankData.Var1, skill.RankData.Var2); // Add damage if the skill is fully charged var dmgMultiplier = skill.RankData.Var4 / 100f; if (attacker.Temp.LightningRodFullCharge) { damage += (damage * dmgMultiplier); } // Critical Hit if (crit) { var bonus = critSkill.RankData.Var1 / 100f; damage = damage + (damage * bonus); tAction.Set(TargetOptions.Critical); } // MDef and MProt SkillHelper.HandleMagicDefenseProtection(target, ref damage); // Conditions SkillHelper.HandleConditions(attacker, target, ref damage); // Mana Deflector var delayReduction = ManaDeflector.Handle(attacker, target, ref damage, tAction); // Mana Shield ManaShield.Handle(target, ref damage, tAction); // Apply Damage target.TakeDamage(tAction.Damage = damage, attacker); // Stun Time tAction.Stun = TargetStun; // Death or Knockback if (target.IsDead) { tAction.Set(TargetOptions.FinishingKnockDown); attacker.Shove(target, KnockbackDistance); } else { // Always knock down if (target.Is(RaceStands.KnockDownable)) { tAction.Set(TargetOptions.KnockDown); attacker.Shove(target, KnockbackDistance); } } } // Update current weapon SkillHelper.UpdateWeapon(attacker, targets.FirstOrDefault(), ProficiencyGainType.Melee, attacker.RightHand); cap.Handle(); Send.Effect(attacker, Effect.LightningRod, (int)LightningRodEffect.Attack, poe.X, poe.Y); Send.SkillUse(attacker, skill.Info.Id, targetAreaId, 0, 1); skill.Train(1); // Use the Skill attacker.Region.RemoveProp(lProp); }