From b0ec1a1bddfc0995fdc3cfb1e73d2ebd2a8d4bb3 Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Fri, 1 Nov 2024 18:31:37 -0400 Subject: [PATCH 1/8] Adding the first few Outlaw skills Brick Smash, Throw Sand, Mangle --- .../Buffs/Handlers/Common/Common_Silence.cs | 19 ++ .../Scouts/Outlaw/BreakBrick_Debuff.cs | 43 +++++ .../Outlaw/MangleAndFireBlindly_Debuff.cs | 37 ++++ .../Handlers/Scouts/Outlaw/Mangle_Buff.cs | 29 +++ .../Scouts/Outlaw/SprinkleSands_Debuff.cs | 42 +++++ src/ZoneServer/Skills/Combat/SkillModifier.cs | 18 +- .../Scouts/Outlaw/OutLaw_BreakBrick.cs | 106 +++++++++++ .../Scouts/Outlaw/OutLaw_SprinkleSands.cs | 101 ++++++++++ .../Handlers/Scouts/Outlaw/Outlaw_Mangle.cs | 177 ++++++++++++++++++ system/scripts/zone/core/calc_combat.cs | 3 + 10 files changed, 571 insertions(+), 4 deletions(-) create mode 100644 src/ZoneServer/Buffs/Handlers/Common/Common_Silence.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs diff --git a/src/ZoneServer/Buffs/Handlers/Common/Common_Silence.cs b/src/ZoneServer/Buffs/Handlers/Common/Common_Silence.cs new file mode 100644 index 000000000..90320f74e --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Common/Common_Silence.cs @@ -0,0 +1,19 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.Components; + +namespace Melia.Zone.Buffs.Handlers.Common +{ + /// + /// Handle for the Silence Debuff, which prevents taking any action + /// + [BuffHandler(BuffId.Common_Silence)] + public class Common_Silence : BuffHandler + { + public override void OnExtend(Buff buff) + { + buff.Target.AddState(StateType.Stunned, buff.Duration); + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs new file mode 100644 index 000000000..7c311ab56 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; + +namespace Melia.Zone.Buffs.Handlers.Scouts.Outlaw +{ + /// + /// Handler for Break Brick Debuff, which reduces Crit Chance + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.BreakBrick_Debuff)] + internal class BreakBrick_Debuff : BuffHandler + { + private const float CRTPenaltyPerLevel = 1f; + + /// + /// Starts buff, flatly reducing Crit rate + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var reduceCrt = buff.NumArg1 * CRTPenaltyPerLevel; + + AddPropertyModifier(buff, buff.Target, PropertyName.CRTHR_BM, -reduceCrt); + } + + /// + /// Ends the buff, resetting Crit rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.CRTHR_BM); + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs new file mode 100644 index 000000000..7ae479ea8 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs @@ -0,0 +1,37 @@ +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Swordsmen.Barbarian +{ + /// + /// Handle for the Mangle and Fire Blindly Debuff, which + /// reduces damage dealt to the inflictor of the buff + /// + /// + /// NumArg1: Skill Level + /// NumArg2: Extra Crit Chance + /// + [BuffHandler(BuffId.MangleAndFireBlindly_Debuff)] + public class MangleAndFireBlindly_Debuff : BuffHandler, IBuffCombatAttackBeforeCalcHandler + { + /// + /// Reduces damage dealt if the target is the caster of the buff + /// + /// + /// + /// + /// + /// + public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + if (target == buff.Caster) + { + modifier.FinalDamageMultiplier -= 0.07f * buff.OverbuffCounter; + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs new file mode 100644 index 000000000..9aa1ce2df --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs @@ -0,0 +1,29 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Swordsmen.Peltasta +{ + /// + /// Handler for the Mangle Buff, which grants forced evade + /// + [BuffHandler(BuffId.Mangle_Buff)] + public class Mangle_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler + { + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.ForcedEvade = true; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs new file mode 100644 index 000000000..bd20bf2c6 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs @@ -0,0 +1,42 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; + +namespace Melia.Zone.Buffs.Handlers.Swordsmen.Barbarian +{ + /// + /// Handler for Sprinkle Sands Debuff, which reduces Accuracy and Evasion + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.SprinkleSands_Debuff)] + public class SprinkleSands_Debuff : BuffHandler + { + private const float HRPenaltyRate = 0.2f; + private const float DRPenaltyRate = 0.2f; + + /// + /// Starts buff, reducing HR and DR + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var reduceHR = buff.Target.Properties.GetFloat(PropertyName.HR) * HRPenaltyRate; + var reduceDR = buff.Target.Properties.GetFloat(PropertyName.DR) * DRPenaltyRate; + + AddPropertyModifier(buff, buff.Target, PropertyName.HR_BM, -reduceHR); + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, -reduceDR); + } + + /// + /// Ends the buff, resetting HR and DR. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.HR_BM); + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + } + } +} diff --git a/src/ZoneServer/Skills/Combat/SkillModifier.cs b/src/ZoneServer/Skills/Combat/SkillModifier.cs index 127e61a0e..18f6ae6b7 100644 --- a/src/ZoneServer/Skills/Combat/SkillModifier.cs +++ b/src/ZoneServer/Skills/Combat/SkillModifier.cs @@ -106,7 +106,16 @@ public class SkillModifier /// /// Gets or sets whether the attack can be blocked. Beats out ForcedBlock. /// - public bool Unblockable { get; set; } + public bool Unblockable { get; set; } + + /// + /// Gets or sets forced block status. + /// + /// + /// If this is true, the attack is always blocked + /// unless it is unblockable. + /// + public bool ForcedBlock { get; set; } /// /// Gets or sets forced hit status. @@ -117,12 +126,13 @@ public class SkillModifier public bool ForcedHit { get; set; } /// - /// Gets or sets forced block status. + /// Gets or sets forced evade status. /// /// - /// If this is true, the attack is always blocked. + /// If this is true, the attack is always evaded + /// unless it is unavoidable /// - public bool ForcedBlock { get; set; } + public bool ForcedEvade { get; set; } /// /// Gets or sets forced critical status. diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs new file mode 100644 index 000000000..5651b0162 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using Yggdrasil.Util; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.Outlaw +{ + /// + /// Handler for the Assassin skill Brick Smash + /// + [SkillHandler(SkillId.OutLaw_BreakBrick)] + public class OutLaw_BreakBrick : IGroundSkillHandler + { + public const float JumpDistance = 60f; + + /// + /// Handles skill + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 90, width: 30, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea, farPos)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea, Position farPos) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + var hitDelay = TimeSpan.FromMilliseconds(400); + + // First perform the jump + var targetPos = caster.Position.GetRelative(caster.Direction, JumpDistance); + targetPos = caster.Map.Ground.GetLastValidPosition(caster.Position, targetPos); + + caster.Position = targetPos; + Send.ZC_NORMAL.LeapJump(caster, targetPos, 0.15f, 0.1f, 1f, 0.2f, 1f, 3); + + await Task.Delay(hitDelay); + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + + // 30% chance to Stun as base, 100% when behind target + var stunChance = 3; + + if (caster.IsBehind(target)) + stunChance = 10; + + if (RandomProvider.Get().Next(10) <= stunChance) + target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(3), caster); + + target.StartBuff(BuffId.BreakBrick_Debuff, skill.Level, 0, TimeSpan.FromSeconds(20), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs new file mode 100644 index 000000000..7038bbab7 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using Yggdrasil.Util; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.Outlaw +{ + /// + /// Handler for the Assassin skill Throw Sand + /// + [SkillHandler(SkillId.OutLaw_SprinkleSands)] + public class OutLaw_SprinkleSands : IGroundSkillHandler + { + /// + /// Handles skill + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 100, width: 40, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea, farPos)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea, Position farPos) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + var hitDelay = TimeSpan.FromMilliseconds(250); + + await Task.Delay(hitDelay); + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(4); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + + target.StartBuff(BuffId.Common_Silence, skill.Level, 0, TimeSpan.FromMilliseconds(200 * skill.Level), caster); + target.StartBuff(BuffId.SprinkleSands_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); + + target.StartBuff(BuffId.DecreaseHeal_Debuff, skill.Level, this.GetHealingReduction(skill), TimeSpan.FromSeconds(5), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + + /// + /// Return the Healing Reduction value + /// + /// + /// + private float GetHealingReduction(Skill skill) + { + return (2.3f * skill.Level) * 1000; + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs new file mode 100644 index 000000000..8fcc73975 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Swordsmen.Doppelsoeldner +{ + /// + /// Handler for the Outlaw skill Mangle + /// + [SkillHandler(SkillId.OutLaw_Mangle)] + public class OutLaw_Mangle : IGroundSkillHandler + { + /// + /// Handles skill, damaging targets. + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 40, width: 20, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) + { + var hitDelay1 = TimeSpan.FromMilliseconds(350); + var hitDelay2 = TimeSpan.FromMilliseconds(600); + var hitDelay3 = TimeSpan.FromMilliseconds(50); + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + + // Outlaw6 gives guaranteed evade throughout the whole animation + if (caster.IsAbilityActive(AbilityId.Outlaw6)) + caster.StartBuff(BuffId.Mangle_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1300), caster); + + // First attack hits with no delay + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(3); + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay1); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(2); + + // Outlaw7 adds 3 more hits + if (caster.IsAbilityActive(AbilityId.Outlaw7)) + modifier.HitCount += 3; + + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay2); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.Default; + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay3); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(3); + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + + target.StartBuff(BuffId.MangleAndFireBlindly_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + + + /// + /// Return the bonus damage + /// Adds 0.5 to final damage for each of Blind, Bleed, or Stun + /// + /// + /// + private float GetDamageBonus(ICombatEntity target) + { + float damageBonus = 0f; + + if (target.IsBuffActive(BuffId.SprinkleSands_Debuff)) + damageBonus += 0.5f; + + if (target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff)) + damageBonus += 0.5f; + + if (target.IsBuffActive(BuffId.Stun)) + damageBonus += 0.5f; + + return damageBonus; + } + } +} diff --git a/system/scripts/zone/core/calc_combat.cs b/system/scripts/zone/core/calc_combat.cs index 412ebbcd0..203e2005f 100644 --- a/system/scripts/zone/core/calc_combat.cs +++ b/system/scripts/zone/core/calc_combat.cs @@ -557,6 +557,9 @@ public float SCR_GetDodgeChance(ICombatEntity attacker, ICombatEntity target, Sk if (modifier.ForcedHit) return 0; + if (modifier.ForcedEvade) + return 100; + var dr = target.Properties.GetFloat(PropertyName.DR); var hr = attacker.Properties.GetFloat(PropertyName.HR); From 3439644bebc6618a63b36f1fc229b443387d1a58 Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Sat, 2 Nov 2024 03:56:10 -0400 Subject: [PATCH 2/8] Adding the rest of the skills All skills implemented --- src/ZoneServer/Buffs/Buff.cs | 11 ++ .../Common/Skill_MomentaryEvasion_Buff.cs | 34 ++++ .../Handlers/Scouts/OutLaw/Aggress_Buff.cs | 37 ++++ .../Handlers/Scouts/OutLaw/Aggress_Debuff.cs | 64 ++++++ .../Scouts/OutLaw/BullyPainBarrier_Buff.cs | 77 ++++++++ .../Handlers/Scouts/OutLaw/Bully_Buff.cs | 76 ++++++++ .../Scouts/OutLaw/FireBlindly_Buff.cs | 29 +++ .../Scouts/OutLaw/Rampage_After_Buff.cs | 51 +++++ .../Scouts/OutLaw/Rampage_Outlaw18_Buff.cs | 36 ++++ .../Scouts/Outlaw/BreakBrick_Debuff.cs | 6 +- .../Outlaw/MangleAndFireBlindly_Debuff.cs | 2 +- .../Handlers/Scouts/Outlaw/Mangle_Buff.cs | 2 +- .../Scouts/Outlaw/SprinkleSands_Debuff.cs | 2 +- src/ZoneServer/Scripting/AI/AiEvent.cs | 23 +++ src/ZoneServer/Scripting/AI/AiScript.cs | 6 + src/ZoneServer/Skills/Combat/SkillHitInfo.cs | 7 + .../Handlers/Scouts/OutLaw/OutLaw_Aggress.cs | 82 ++++++++ .../Handlers/Scouts/OutLaw/OutLaw_Bully.cs | 49 +++++ .../Scouts/OutLaw/OutLaw_FireBlindly.cs | 135 +++++++++++++ .../Handlers/Scouts/OutLaw/OutLaw_Rampage.cs | 183 ++++++++++++++++++ .../Scouts/Outlaw/OutLaw_BreakBrick.cs | 2 +- .../Scouts/Outlaw/OutLaw_SprinkleSands.cs | 2 +- .../Handlers/Scouts/Outlaw/Outlaw_Mangle.cs | 2 +- .../Components/BuffComponent.cs | 3 + 24 files changed, 912 insertions(+), 9 deletions(-) create mode 100644 src/ZoneServer/Buffs/Handlers/Common/Skill_MomentaryEvasion_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs diff --git a/src/ZoneServer/Buffs/Buff.cs b/src/ZoneServer/Buffs/Buff.cs index 08d87024b..c7f11b28d 100644 --- a/src/ZoneServer/Buffs/Buff.cs +++ b/src/ZoneServer/Buffs/Buff.cs @@ -267,6 +267,17 @@ internal void ExtendDuration() this.NextUpdateTime = DateTime.Now.Add(this.Data.UpdateTime); } + /// + /// Increases the buff's duration by a given amount. + /// + internal void IncreaseDuration(TimeSpan amount) + { + if (this.HasDuration) + { + this.RemovalTime = DateTime.Now.Add(amount); + } + } + /// /// Executes the buff handler's end behavior. Does not actually /// end or remove the buff. diff --git a/src/ZoneServer/Buffs/Handlers/Common/Skill_MomentaryEvasion_Buff.cs b/src/ZoneServer/Buffs/Handlers/Common/Skill_MomentaryEvasion_Buff.cs new file mode 100644 index 000000000..db6711139 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Common/Skill_MomentaryEvasion_Buff.cs @@ -0,0 +1,34 @@ +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Common +{ + /// + /// Contains code related to the Momentary Block Buff + /// + /// + /// This buff is granted by certain skills and causes you to always + /// evade for a duration. + /// + [BuffHandler(BuffId.Skill_MomentaryEvasion_Buff)] + public class Skill_MomentaryEvasion_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler + { + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.ForcedEvade = true; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs new file mode 100644 index 000000000..9c063e2e1 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs @@ -0,0 +1,37 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Network; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for Aggress Buff, which massively increases movement speed + /// + [BuffHandler(BuffId.Aggress_Buff)] + public class Aggress_Buff : BuffHandler + { + private const float MspdBonus = 30f; + + /// + /// Starts buff, modifying the movement speed + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var target = buff.Target; + + AddPropertyModifier(buff, target, PropertyName.MSPD_BM, MspdBonus); + Send.ZC_MSPD(target); + } + + /// + /// Ends the buff, resetting the movement speed + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.MSPD_BM); + Send.ZC_MSPD(buff.Target); + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs new file mode 100644 index 000000000..1bae39d3c --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Network; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for Break Brick Debuff, which reduces Evasion + /// + /// + /// NumArg1: Skill Level + /// NumArg2: Unhinge Level + /// This is related to Outlaw13, which changes the buff effect to + /// also increase movement speed but decrease accuracy + /// + [BuffHandler(BuffId.Aggress_Debuff)] + internal class Aggress_Debuff : BuffHandler + { + private const float DRPenaltyPerLevel = 0.02f; + private const float HRPenaltyPerLevel = 0.03f; + private const float MspdBonus = 5f; + + /// + /// Starts buff, reducing Dodge Rate + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var reduceDR = buff.Target.Properties.GetFloat(PropertyName.DR) * buff.NumArg1 * DRPenaltyPerLevel; + + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, -reduceDR); + + if (buff.NumArg2 > 0) + { + var reduceHR = buff.Target.Properties.GetFloat(PropertyName.HR) * buff.NumArg2 * HRPenaltyPerLevel; + + AddPropertyModifier(buff, buff.Target, PropertyName.HR_BM, -reduceHR); + AddPropertyModifier(buff, buff.Target, PropertyName.MSPD_BM, MspdBonus); + Send.ZC_MSPD(buff.Target); + } + } + + /// + /// Ends the buff, resetting dodge rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + + if (buff.NumArg2 > 0) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.HR_BM); + RemovePropertyModifier(buff, buff.Target, PropertyName.MSPD_BM); + Send.ZC_MSPD(buff.Target); + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs new file mode 100644 index 000000000..39af8e442 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs @@ -0,0 +1,77 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Scripting.AI; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Melia.Zone.World.Actors.Monsters; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Buff handler for Bully Pain Barrier Buff, which increases evasion + /// and adds threat on successful evade + /// It's completely identical to Bully Buff except that it also + /// grants immunity to knockback and Outlaw12's effect is nerfed + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.BullyPainBarrier_Buff)] + public class BullyPainBarrier_Buff : BuffHandler, IBuffCombatDefenseAfterCalcHandler + { + private const float DrBuffRateBase = 0.24f; + private const float DrBuffRatePerLevel = 0.04f; + private const float HatePerLevel = 3f; + + /// + /// Starts buff, increasing dodge rate. + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var dr = buff.Target.Properties.GetFloat(PropertyName.DR); + var skillLevel = buff.NumArg1; + var rate = DrBuffRateBase + DrBuffRatePerLevel * skillLevel; + var bonus = dr * rate; + + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, bonus); + } + + /// + /// Ends the buff, resetting dodge rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + } + + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseAfterCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + if (skillHitResult.Result == HitResultType.Dodge && attacker.Components.TryGet(out var component)) + { + component.Script.QueueEventAlert(new HateIncreaseAlert(target, buff.NumArg1 * HatePerLevel)); + + // Outlaw12 adds additional duration to the buff on successful evade + // For Pain Barrier buff, the maximum increase is 2 seconds + if (target.TryGetActiveAbilityLevel(AbilityId.Outlaw12, out var level)) + { + buff.IncreaseDuration(TimeSpan.FromSeconds(Math.Max(level, 2))); + } + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs new file mode 100644 index 000000000..21e1dd388 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs @@ -0,0 +1,76 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Scripting.AI; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Melia.Zone.World.Actors.Monsters; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Buff handler for Bully Buff, which increases evasion + /// and adds threat on successful evade + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.Bully_Buff)] + public class Bully_Buff : BuffHandler, IBuffCombatDefenseAfterCalcHandler + { + private const float DrBuffRateBase = 0.24f; + private const float DrBuffRatePerLevel = 0.04f; + private const float HatePerLevel = 3f; + + /// + /// Starts buff, increasing dodge rate. + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var dr = buff.Target.Properties.GetFloat(PropertyName.DR); + var skillLevel = buff.NumArg1; + var rate = DrBuffRateBase + DrBuffRatePerLevel * skillLevel; + var bonus = dr * rate; + + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, bonus); + } + + /// + /// Ends the buff, resetting dodge rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + } + + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseAfterCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + if (skillHitResult.Result == HitResultType.Dodge && attacker.Components.TryGet(out var component)) + { + component.Script.QueueEventAlert(new HateIncreaseAlert(target, buff.NumArg1 * HatePerLevel)); + + // Outlaw12 adds additional duration to the buff on successful evade + // Note that it only applies to monster attacks, which is why + // it's done after the AI script check + if (target.TryGetActiveAbilityLevel(AbilityId.Outlaw12, out var level)) + { + buff.IncreaseDuration(TimeSpan.FromSeconds(level)); + } + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs new file mode 100644 index 000000000..a65d9258c --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs @@ -0,0 +1,29 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Fire Blindly Buff, which grants forced evade + /// + [BuffHandler(BuffId.FireBlindly_Buff)] + public class FireBlindly_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler + { + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.ForcedEvade = true; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs new file mode 100644 index 000000000..2ca0bc548 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs @@ -0,0 +1,51 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handle for the Rampage After Buff, which increases damage + /// dealt and taken by 50% and prevents the caster from evading + /// + /// + /// NumArg1: Level + /// NumArg2: None + /// + [BuffHandler(BuffId.Rampage_After_Buff)] + public class Rampage_After_Buff : BuffHandler, IBuffCombatAttackBeforeCalcHandler, IBuffCombatDefenseBeforeCalcHandler + { + private const float DamageBonus = 0.5f; + private const float DamagePenalty = 0.5f; + + /// + /// Adds the damage bonus + /// + /// + /// + /// + /// + /// + public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.FinalDamageMultiplier += DamageBonus; + } + + /// + /// Adds the damage penalty and prevents evasion + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.FinalDamageMultiplier += DamagePenalty; + modifier.ForcedHit = true; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs new file mode 100644 index 000000000..65fec8563 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs @@ -0,0 +1,36 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handle for the Rampage Outlaw18 Buff, which gives 10% + /// bonus crit chance per debuff that was active when it + /// was started (which was calculated when it was applied) + /// + /// + /// NumArg1: Debuff Count + /// NumArg2: None + /// + [BuffHandler(BuffId.Rampage_Outlaw18_Buff)] + public class Rampage_Outlaw18_Buff : BuffHandler, IBuffCombatAttackBeforeCalcHandler + { + public const float CritBonusPerDebuff = 10f; + + /// + /// Adds the bonus crit chance + /// + /// + /// + /// + /// + /// + public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.BonusCritChance = CritBonusPerDebuff * buff.NumArg1; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs index 7c311ab56..34948b5bb 100644 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs @@ -6,7 +6,7 @@ using Melia.Shared.Game.Const; using Melia.Zone.Buffs.Base; -namespace Melia.Zone.Buffs.Handlers.Scouts.Outlaw +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw { /// /// Handler for Break Brick Debuff, which reduces Crit Chance @@ -21,12 +21,12 @@ internal class BreakBrick_Debuff : BuffHandler private const float CRTPenaltyPerLevel = 1f; /// - /// Starts buff, flatly reducing Crit rate + /// Starts buff, reducing Crit rate /// /// public override void OnActivate(Buff buff, ActivationType activationType) { - var reduceCrt = buff.NumArg1 * CRTPenaltyPerLevel; + var reduceCrt = buff.Target.Properties.GetFloat(PropertyName.CRTHR) * buff.NumArg1 * CRTPenaltyPerLevel; AddPropertyModifier(buff, buff.Target, PropertyName.CRTHR_BM, -reduceCrt); } diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs index 7ae479ea8..c059b2f8a 100644 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs @@ -5,7 +5,7 @@ using Melia.Zone.Skills.Combat; using Melia.Zone.World.Actors; -namespace Melia.Zone.Buffs.Handlers.Swordsmen.Barbarian +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw { /// /// Handle for the Mangle and Fire Blindly Debuff, which diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs index 9aa1ce2df..39399b03d 100644 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs @@ -4,7 +4,7 @@ using Melia.Zone.Skills.Combat; using Melia.Zone.World.Actors; -namespace Melia.Zone.Buffs.Handlers.Swordsmen.Peltasta +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw { /// /// Handler for the Mangle Buff, which grants forced evade diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs index bd20bf2c6..0c2a32b79 100644 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs +++ b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs @@ -1,7 +1,7 @@ using Melia.Shared.Game.Const; using Melia.Zone.Buffs.Base; -namespace Melia.Zone.Buffs.Handlers.Swordsmen.Barbarian +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw { /// /// Handler for Sprinkle Sands Debuff, which reduces Accuracy and Evasion diff --git a/src/ZoneServer/Scripting/AI/AiEvent.cs b/src/ZoneServer/Scripting/AI/AiEvent.cs index ce8fe672c..935fd2f7e 100644 --- a/src/ZoneServer/Scripting/AI/AiEvent.cs +++ b/src/ZoneServer/Scripting/AI/AiEvent.cs @@ -59,4 +59,27 @@ public HateResetAlert(ICombatEntity target) this.Target = target; } } + + public class HateIncreaseAlert : IAiEventAlert + { + /// + /// Returns the entity for which the hate should increase + /// + public ICombatEntity Target { get; } + + /// + /// Returns the amount of hate to gain. + /// + public float Amount { get; } + + /// + /// Creates new event. + /// + /// + public HateIncreaseAlert(ICombatEntity target, float amount) + { + this.Target = target; + this.Amount = amount; + } + } } diff --git a/src/ZoneServer/Scripting/AI/AiScript.cs b/src/ZoneServer/Scripting/AI/AiScript.cs index 30bf184e4..9df141991 100644 --- a/src/ZoneServer/Scripting/AI/AiScript.cs +++ b/src/ZoneServer/Scripting/AI/AiScript.cs @@ -488,6 +488,12 @@ private void ReactToAlert(IAiEventAlert eventAlert) _hateLevels.Remove(targetHandle); break; } + + case HateIncreaseAlert hateIncreaseAlert: + { + this.IncreaseHate(hateIncreaseAlert.Target, hateIncreaseAlert.Amount); + break; + } } } diff --git a/src/ZoneServer/Skills/Combat/SkillHitInfo.cs b/src/ZoneServer/Skills/Combat/SkillHitInfo.cs index b0c8d8487..50f28dbe0 100644 --- a/src/ZoneServer/Skills/Combat/SkillHitInfo.cs +++ b/src/ZoneServer/Skills/Combat/SkillHitInfo.cs @@ -99,6 +99,13 @@ public void ApplyKnockBack(ICombatEntity target) if (this.KnockBackInfo == null) throw new InvalidOperationException("Knock back info is not set."); + // Knockback immunity check - may need to move this + if (target.IsBuffActive(BuffId.BullyPainBarrier_Buff)) + { + this.KnockBackInfo = null; + return; + } + var isKnockBack = this.KnockBackInfo.HitType == HitType.KnockBack; var isKnockDown = this.KnockBackInfo.HitType == HitType.KnockDown; diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs new file mode 100644 index 000000000..955d75c69 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs @@ -0,0 +1,82 @@ +using System; +using Melia.Shared.L10N; +using Melia.Shared.Game.Const; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Melia.Shared.Data.Database; +using Melia.Zone.Scripting.AI; +using Melia.Zone.Skills.SplashAreas; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Aggress. + /// + [SkillHandler(SkillId.OutLaw_Aggress)] + public class OutLaw_Aggress : IGroundSkillHandler + { + /// + /// Handles the skill, debuffing enemies in the target area. + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + Send.ZC_SKILL_MELEE_GROUND(caster, skill, caster.Position, null); + + var duration = TimeSpan.FromSeconds(10); + + // Splash area is a square of length 300, width 100, starting 100 + // units behind the caster + var splashArea = new Square(caster.Position.GetRelative(caster.Direction, -100f), caster.Direction, 200f, 100f); + Debug.ShowShape(caster.Map, splashArea); + + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + var maxTargets = Math.Min(targets.Count, 3 * skill.Level); + + var unhingeLevel = 0f; + + if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw13, out var level)) + unhingeLevel = level; + + for (var i = 0; i < maxTargets; i++) + { + var target = targets[i]; + if (target.IsBuffActive(BuffId.ProvocationImmunity_Debuff)) + continue; + + target.StartBuff(BuffId.Aggress_Debuff, skill.Level, unhingeLevel, duration, caster); + target.StartBuff(BuffId.ProvocationImmunity_Debuff, skill.Level, 0, duration, caster); + + if (target.Components.TryGet(out var component)) + { + // Reset hate and simulate a hit to build a small amount + // of threat + component.Script.QueueEventAlert(new HateResetAlert(caster)); + component.Script.QueueEventAlert(new HitEventAlert(target, caster, 0)); + } + } + + // Outlaw14 adds a massive speed buff + if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw14, out var getawayLevel)) + { + caster.StartBuff(BuffId.Aggress_Buff, skill.Level, 0, TimeSpan.FromSeconds(getawayLevel), caster); + } + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs new file mode 100644 index 000000000..6cfb455c7 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs @@ -0,0 +1,49 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Bully. + /// + [SkillHandler(SkillId.OutLaw_Bully)] + public class OutLaw_Bully : ISelfSkillHandler + { + /// + /// Handles skill, applying a buff to the caster. + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Direction dir) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + // Uses a totally different buff if Outlaw19 is active + if (caster.IsAbilityActive(AbilityId.Outlaw19)) + { + caster.StartBuff(BuffId.BullyPainBarrier_Buff, skill.Level, 0, TimeSpan.FromSeconds(20), caster); + } + else + { + caster.StartBuff(BuffId.Bully_Buff, skill.Level, 0, TimeSpan.FromSeconds(60), caster); + } + + Send.ZC_SKILL_MELEE_TARGET(caster, skill, caster, null); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs new file mode 100644 index 000000000..fba7199c9 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Blindfire + /// + [SkillHandler(SkillId.OutLaw_FireBlindly)] + public class OutLaw_FireBlindly : IGroundSkillHandler + { + /// + /// Handles skill, damaging targets. + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 120, width: 40, angle: 80); + var splashArea = skill.GetSplashArea(SplashType.Fan, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) + { + var hitDelay = TimeSpan.FromMilliseconds(360); + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + + // Outlaw9 gives guaranteed evade throughout the whole animation + if (caster.IsAbilityActive(AbilityId.Outlaw9)) + caster.StartBuff(BuffId.FireBlindly_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(600), caster); + + // First attack hits with no delay + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(4); + modifier.BonusCritChance += GetCritBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + + // Outlaw10 adds 30% damage on a critical + if (caster.IsAbilityActive(AbilityId.Outlaw10) && skillHitResult.Result == HitResultType.Crit) + skillHitResult.Damage *= 1.3f; + + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(4); + modifier.BonusCritChance += GetCritBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + + // Outlaw10 adds 30% damage on a critical + if (caster.IsAbilityActive(AbilityId.Outlaw10) && skillHitResult.Result == HitResultType.Crit) + skillHitResult.Damage *= 1.3f; + + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + + // Description doesn't mention this, but it's in skill_bytool + target.StartBuff(BuffId.MangleAndFireBlindly_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + + + /// + /// Return the bonus damage + /// Adds 20 to crit chance if target has any of Blind, Bleed, or Stun + /// + /// + /// + private float GetCritBonus(ICombatEntity target) + { + if (target.IsBuffActive(BuffId.SprinkleSands_Debuff) || target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff) || target.IsBuffActive(BuffId.Stun)) + return 20f; + + return 0f; + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs new file mode 100644 index 000000000..062e27db8 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Yggdrasil.Logging; +using Yggdrasil.Util; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Rampage. + /// + [SkillHandler(SkillId.OutLaw_Rampage)] + public class OutLaw_Rampage : IGroundSkillHandler + { + private const float BuffRemoveChancePerLevel = 3f; + + /// + /// Handles skill, damaging targets. + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 0, width: 150, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Circle, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + + var delayBetweenHits = new[] + { + TimeSpan.FromMilliseconds(50), + TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(500), + TimeSpan.FromMilliseconds(50), + TimeSpan.FromMilliseconds(700), + TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(50), + TimeSpan.FromMilliseconds(50) + }; + + // first hit hits instantly + + var hits = new List(); + + var stunChance = 0f; + if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw16, out int level)) + stunChance = level; + + var iceVariant = caster.IsAbilityActive(AbilityId.Outlaw20); + + // Caster avoids all attacks during the skill's animation + caster.StartBuff(BuffId.Skill_MomentaryEvasion_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1700), caster); + + // Outlaw17 gives a buff that prevents removable debuffs + if (caster.IsAbilityActive(AbilityId.Outlaw17)) + caster.StartBuff(BuffId.Rampage_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1700), caster); + + // Outlaw18 gives a buff that adds 10% bonus crit chance per debuff currently active + if (caster.IsAbilityActive(AbilityId.Outlaw18)) + { + int debuffCount = 0; + foreach (var buff in caster.Components.Get().GetList()) + { + if (buff.Data.Type == BuffType.Debuff) + { + debuffCount++; + } + } + + if (debuffCount > 0) + caster.StartBuff(BuffId.Rampage_Outlaw18_Buff, debuffCount, 0, TimeSpan.FromMilliseconds(1700), caster); + } + + for (var i = 0; i < 9; i++) + { + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.Default; + + // Targets with certain statuses take 2 hits at 30% less damage + if (GetDoubleHit(target)) + { + modifier.HitCount = 2; + modifier.FinalDamageMultiplier -= 0.3f; + } + + if (iceVariant) + modifier.AttackAttribute = AttributeType.Ice; + + Log.Warning("Attempting to hit target"); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + + Log.Warning("Hit target"); + + var buffRemoveChance = BuffRemoveChancePerLevel * skill.Level; + if (RandomProvider.Get().Next(1000) < buffRemoveChance) + { + target.RemoveRandomBuff(); + } + + if (RandomProvider.Get().Next(100) < stunChance) + { + target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(2), caster); + } + + // Ice effect is only applied on last hit + if (i == 8 && iceVariant) + { + // TODO: this ability states it can knock down instead + target.StartBuff(BuffId.Freeze, skill.Level, 0, TimeSpan.FromSeconds(2), caster); + } + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + + hits.Clear(); + + if (i < 8) + await Task.Delay(delayBetweenHits[i]); + } + + caster.StartBuff(BuffId.Rampage_After_Buff, skill.Level, 0, TimeSpan.FromSeconds(5), caster); + } + + + /// + /// Checks if a target takes 2 hits + /// This occurs if they have any of Blind, Bleed, or Stun + /// + /// + /// + private bool GetDoubleHit(ICombatEntity target) + { + return target.IsBuffActive(BuffId.SprinkleSands_Debuff) || target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff) || target.IsBuffActive(BuffId.Stun); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs index 5651b0162..7da451c42 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs @@ -14,7 +14,7 @@ using static Melia.Shared.Util.TaskHelper; using static Melia.Zone.Skills.SkillUseFunctions; -namespace Melia.Zone.Skills.Handlers.Scouts.Outlaw +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw { /// /// Handler for the Assassin skill Brick Smash diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs index 7038bbab7..b842fe431 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs @@ -14,7 +14,7 @@ using static Melia.Shared.Util.TaskHelper; using static Melia.Zone.Skills.SkillUseFunctions; -namespace Melia.Zone.Skills.Handlers.Scouts.Outlaw +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw { /// /// Handler for the Assassin skill Throw Sand diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs index 8fcc73975..a99b7b579 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs @@ -13,7 +13,7 @@ using static Melia.Shared.Util.TaskHelper; using static Melia.Zone.Skills.SkillUseFunctions; -namespace Melia.Zone.Skills.Handlers.Swordsmen.Doppelsoeldner +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw { /// /// Handler for the Outlaw skill Mangle diff --git a/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs b/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs index 12d995c29..0b3233e8f 100644 --- a/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs +++ b/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs @@ -369,6 +369,9 @@ private bool TryResistDebuff(BuffId buffId, ICombatEntity caster) if (this.Has(BuffId.Skill_MomentaryImmune_Buff)) return true; + if (this.Has(BuffId.Rampage_Buff) && buffData.Removable) + return true; + if (this.TryGet(BuffId.Cyclone_Buff_ImmuneAbil, out var cycloneImmuneBuff)) { if (RandomProvider.Get().Next(100) < cycloneImmuneBuff.NumArg1 * 15) From 6d1653752fa0abdcb089c6833bd1523aa6957c52 Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Sat, 2 Nov 2024 04:21:31 -0400 Subject: [PATCH 3/8] Adding the rest of the abilities All abilities implemented --- .../Handlers/Scouts/OutLaw/OutLaw_Rampage.cs | 4 - .../Scouts/Outlaw/OutLaw_BreakBrick.cs | 141 +++++++++++++++++- .../Scouts/Outlaw/OutLaw_SprinkleSands.cs | 10 +- 3 files changed, 143 insertions(+), 12 deletions(-) diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs index 062e27db8..6a3b075f7 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs @@ -127,8 +127,6 @@ private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashA if (iceVariant) modifier.AttackAttribute = AttributeType.Ice; - Log.Warning("Attempting to hit target"); - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); target.TakeDamage(skillHitResult.Damage, caster); @@ -136,8 +134,6 @@ private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashA skillHit.HitEffect = HitEffect.Impact; hits.Add(skillHit); - Log.Warning("Hit target"); - var buffRemoveChance = BuffRemoveChancePerLevel * skill.Level; if (RandomProvider.Get().Next(1000) < buffRemoveChance) { diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs index 7da451c42..f8f19f2bf 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs @@ -49,7 +49,17 @@ public void Handle(Skill skill, ICombatEntity caster, Position originPos, Positi Send.ZC_SKILL_READY(caster, skill, originPos, farPos); Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); - CallSafe(this.Attack(skill, caster, splashArea, farPos)); + // Outlaw26 is a totally different attack + if (caster.IsAbilityActive(AbilityId.Outlaw26)) + { + // This should set the overheat max to 1 + + CallSafe(this.ShatterAttack(skill, caster, farPos)); + } + else + { + CallSafe(this.Attack(skill, caster, splashArea, farPos)); + } } /// @@ -71,6 +81,10 @@ private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashA caster.Position = targetPos; Send.ZC_NORMAL.LeapJump(caster, targetPos, 0.15f, 0.1f, 1f, 0.2f, 1f, 3); + // Outlaw4 has a 20% chance to activate. If it does, it guarantees + // the stun and makes the attack a forced critical + var outlaw4Activates = caster.IsAbilityActive(AbilityId.Outlaw4) && RandomProvider.Get().Next(5) == 1; + await Task.Delay(hitDelay); var hits = new List(); @@ -80,6 +94,18 @@ private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashA { var modifier = SkillModifier.MultiHit(6); + // 30% chance to Stun as base, 100% when behind target + var stunChance = 3; + + if (caster.IsBehind(target)) + stunChance = 10; + + if (outlaw4Activates) + { + stunChance = 10; + modifier.ForcedCritical = true; + } + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); target.TakeDamage(skillHitResult.Damage, caster); @@ -88,12 +114,6 @@ private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashA hits.Add(skillHit); - // 30% chance to Stun as base, 100% when behind target - var stunChance = 3; - - if (caster.IsBehind(target)) - stunChance = 10; - if (RandomProvider.Get().Next(10) <= stunChance) target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(3), caster); @@ -102,5 +122,112 @@ private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashA Send.ZC_SKILL_HIT_INFO(caster, hits); } + + /// + /// Executes the alternate attack for Outlaw26 + /// It doesn't inflict status but has a bigger range + /// and does 3 times as many hits + /// + /// + /// + /// + private async Task ShatterAttack(Skill skill, ICombatEntity caster, Position farPos) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + var hitDelay = TimeSpan.FromMilliseconds(250); + + // This version has no jump + + // Outlaw4 has a 20% chance to activate. If it does, it inflicts + // stun and forces a critical + var outlaw4Activates = caster.IsAbilityActive(AbilityId.Outlaw4) && RandomProvider.Get().Next(5) == 1; + + await Task.Delay(hitDelay); + + // This attack uses 3 hitboxes of different shapes, which all hit simultaneously + + var splashParam = skill.GetSplashParameters(caster, caster.Position, farPos, length: 135, width: 30, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + // Outlaw4 has a 20% chance to activate. If it does, it causes + // a stun and makes the attack a forced critical + if (outlaw4Activates) + { + target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(3), caster); + modifier.ForcedCritical = true; + } + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + + // The second hitbox is identical to the first + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + if (outlaw4Activates) + { + modifier.ForcedCritical = true; + } + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + + // The third hitbox is a fan + + splashParam = skill.GetSplashParameters(caster, caster.Position, farPos, length: 150, width: 100, angle: 78); + splashArea = skill.GetSplashArea(SplashType.Fan, splashParam); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + if (outlaw4Activates) + { + modifier.ForcedCritical = true; + } + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } } } diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs index b842fe431..b46bf2c23 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs @@ -67,7 +67,15 @@ private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashA var hits = new List(); var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - foreach (var target in targets.LimitBySDR(caster, skill)) + var hitTargets = targets.LimitBySDR(caster, skill); + + // Outlaw2 makes the max target count 17 + if (caster.IsAbilityActive(AbilityId.Outlaw2)) + { + hitTargets = targets.LimitRandom(17); + } + + foreach (var target in hitTargets) { var modifier = SkillModifier.MultiHit(4); From c64ed2eaaaad09803db570b6fe795867723e9366 Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:42:20 -0500 Subject: [PATCH 4/8] Fixing the file name for mangle --- src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs index a99b7b579..c967a4e97 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs @@ -16,7 +16,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw { /// - /// Handler for the Outlaw skill Mangle + /// Handler for the Outlaw skill Mangle. /// [SkillHandler(SkillId.OutLaw_Mangle)] public class OutLaw_Mangle : IGroundSkillHandler From 69d4ba8530551fec1145221f4b04dc836905e43f Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:46:53 -0500 Subject: [PATCH 5/8] Cleaning up folders --- .../Handlers/Scouts/OutLaw/OutLaw_Aggress.cs | 82 ------ .../Handlers/Scouts/OutLaw/OutLaw_Bully.cs | 49 ---- .../Scouts/OutLaw/OutLaw_FireBlindly.cs | 135 ---------- .../Handlers/Scouts/OutLaw/OutLaw_Rampage.cs | 179 -------------- .../Scouts/Outlaw/OutLaw_BreakBrick.cs | 233 ------------------ .../Scouts/Outlaw/OutLaw_SprinkleSands.cs | 109 -------- .../Handlers/Scouts/Outlaw/Outlaw_Mangle.cs | 177 ------------- 7 files changed, 964 deletions(-) delete mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs delete mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs delete mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs delete mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs delete mode 100644 src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs delete mode 100644 src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs delete mode 100644 src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs deleted file mode 100644 index 955d75c69..000000000 --- a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using Melia.Shared.L10N; -using Melia.Shared.Game.Const; -using Melia.Shared.World; -using Melia.Zone.Network; -using Melia.Zone.Skills.Handlers.Base; -using Melia.Zone.World.Actors; -using Melia.Zone.World.Actors.CombatEntities.Components; -using Melia.Shared.Data.Database; -using Melia.Zone.Scripting.AI; -using Melia.Zone.Skills.SplashAreas; - -namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Outlaw skill Aggress. - /// - [SkillHandler(SkillId.OutLaw_Aggress)] - public class OutLaw_Aggress : IGroundSkillHandler - { - /// - /// Handles the skill, debuffing enemies in the target area. - /// - /// - /// - /// - /// - /// - public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) - { - if (!caster.TrySpendSp(skill)) - { - caster.ServerMessage(Localization.Get("Not enough SP.")); - return; - } - - skill.IncreaseOverheat(); - caster.SetAttackState(true); - - Send.ZC_SKILL_MELEE_GROUND(caster, skill, caster.Position, null); - - var duration = TimeSpan.FromSeconds(10); - - // Splash area is a square of length 300, width 100, starting 100 - // units behind the caster - var splashArea = new Square(caster.Position.GetRelative(caster.Direction, -100f), caster.Direction, 200f, 100f); - Debug.ShowShape(caster.Map, splashArea); - - var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - var maxTargets = Math.Min(targets.Count, 3 * skill.Level); - - var unhingeLevel = 0f; - - if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw13, out var level)) - unhingeLevel = level; - - for (var i = 0; i < maxTargets; i++) - { - var target = targets[i]; - if (target.IsBuffActive(BuffId.ProvocationImmunity_Debuff)) - continue; - - target.StartBuff(BuffId.Aggress_Debuff, skill.Level, unhingeLevel, duration, caster); - target.StartBuff(BuffId.ProvocationImmunity_Debuff, skill.Level, 0, duration, caster); - - if (target.Components.TryGet(out var component)) - { - // Reset hate and simulate a hit to build a small amount - // of threat - component.Script.QueueEventAlert(new HateResetAlert(caster)); - component.Script.QueueEventAlert(new HitEventAlert(target, caster, 0)); - } - } - - // Outlaw14 adds a massive speed buff - if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw14, out var getawayLevel)) - { - caster.StartBuff(BuffId.Aggress_Buff, skill.Level, 0, TimeSpan.FromSeconds(getawayLevel), caster); - } - } - } -} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs deleted file mode 100644 index 6cfb455c7..000000000 --- a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using Melia.Shared.Game.Const; -using Melia.Shared.L10N; -using Melia.Shared.World; -using Melia.Zone.Network; -using Melia.Zone.Skills.Handlers.Base; -using Melia.Zone.World.Actors; - -namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Outlaw skill Bully. - /// - [SkillHandler(SkillId.OutLaw_Bully)] - public class OutLaw_Bully : ISelfSkillHandler - { - /// - /// Handles skill, applying a buff to the caster. - /// - /// - /// - /// - /// - /// - public void Handle(Skill skill, ICombatEntity caster, Position originPos, Direction dir) - { - if (!caster.TrySpendSp(skill)) - { - caster.ServerMessage(Localization.Get("Not enough SP.")); - return; - } - - skill.IncreaseOverheat(); - caster.SetAttackState(true); - - // Uses a totally different buff if Outlaw19 is active - if (caster.IsAbilityActive(AbilityId.Outlaw19)) - { - caster.StartBuff(BuffId.BullyPainBarrier_Buff, skill.Level, 0, TimeSpan.FromSeconds(20), caster); - } - else - { - caster.StartBuff(BuffId.Bully_Buff, skill.Level, 0, TimeSpan.FromSeconds(60), caster); - } - - Send.ZC_SKILL_MELEE_TARGET(caster, skill, caster, null); - } - } -} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs deleted file mode 100644 index fba7199c9..000000000 --- a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Melia.Shared.Data.Database; -using Melia.Shared.Game.Const; -using Melia.Shared.L10N; -using Melia.Shared.World; -using Melia.Zone.Network; -using Melia.Zone.Skills.Combat; -using Melia.Zone.Skills.Handlers.Base; -using Melia.Zone.Skills.SplashAreas; -using Melia.Zone.World.Actors; -using static Melia.Shared.Util.TaskHelper; -using static Melia.Zone.Skills.SkillUseFunctions; - -namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Outlaw skill Blindfire - /// - [SkillHandler(SkillId.OutLaw_FireBlindly)] - public class OutLaw_FireBlindly : IGroundSkillHandler - { - /// - /// Handles skill, damaging targets. - /// - /// - /// - /// - /// - public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) - { - if (!caster.TrySpendSp(skill)) - { - caster.ServerMessage(Localization.Get("Not enough SP.")); - return; - } - - skill.IncreaseOverheat(); - caster.SetAttackState(true); - - var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 120, width: 40, angle: 80); - var splashArea = skill.GetSplashArea(SplashType.Fan, splashParam); - - Send.ZC_SKILL_READY(caster, skill, originPos, farPos); - Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); - - CallSafe(this.Attack(skill, caster, splashArea)); - } - - /// - /// Executes the actual attack after a delay. - /// - /// - /// - /// - private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) - { - var hitDelay = TimeSpan.FromMilliseconds(360); - var damageDelay = TimeSpan.FromMilliseconds(50); - var skillHitDelay = TimeSpan.Zero; - - // Outlaw9 gives guaranteed evade throughout the whole animation - if (caster.IsAbilityActive(AbilityId.Outlaw9)) - caster.StartBuff(BuffId.FireBlindly_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(600), caster); - - // First attack hits with no delay - - var hits = new List(); - var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(4); - modifier.BonusCritChance += GetCritBonus(target); - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - - // Outlaw10 adds 30% damage on a critical - if (caster.IsAbilityActive(AbilityId.Outlaw10) && skillHitResult.Result == HitResultType.Crit) - skillHitResult.Damage *= 1.3f; - - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - hits.Add(skillHit); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - hits.Clear(); - await Task.Delay(hitDelay); - - targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(4); - modifier.BonusCritChance += GetCritBonus(target); - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - - // Outlaw10 adds 30% damage on a critical - if (caster.IsAbilityActive(AbilityId.Outlaw10) && skillHitResult.Result == HitResultType.Crit) - skillHitResult.Damage *= 1.3f; - - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - hits.Add(skillHit); - - // Description doesn't mention this, but it's in skill_bytool - target.StartBuff(BuffId.MangleAndFireBlindly_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - } - - - /// - /// Return the bonus damage - /// Adds 20 to crit chance if target has any of Blind, Bleed, or Stun - /// - /// - /// - private float GetCritBonus(ICombatEntity target) - { - if (target.IsBuffActive(BuffId.SprinkleSands_Debuff) || target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff) || target.IsBuffActive(BuffId.Stun)) - return 20f; - - return 0f; - } - } -} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs deleted file mode 100644 index 6a3b075f7..000000000 --- a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs +++ /dev/null @@ -1,179 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Melia.Shared.Data.Database; -using Melia.Shared.Game.Const; -using Melia.Shared.L10N; -using Melia.Shared.World; -using Melia.Zone.Network; -using Melia.Zone.Skills.Combat; -using Melia.Zone.Skills.Handlers.Base; -using Melia.Zone.Skills.SplashAreas; -using Melia.Zone.World.Actors; -using Melia.Zone.World.Actors.CombatEntities.Components; -using Yggdrasil.Logging; -using Yggdrasil.Util; -using static Melia.Shared.Util.TaskHelper; -using static Melia.Zone.Skills.SkillUseFunctions; - -namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Outlaw skill Rampage. - /// - [SkillHandler(SkillId.OutLaw_Rampage)] - public class OutLaw_Rampage : IGroundSkillHandler - { - private const float BuffRemoveChancePerLevel = 3f; - - /// - /// Handles skill, damaging targets. - /// - /// - /// - /// - /// - public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) - { - if (!caster.TrySpendSp(skill)) - { - caster.ServerMessage(Localization.Get("Not enough SP.")); - return; - } - - skill.IncreaseOverheat(); - caster.SetAttackState(true); - - var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 0, width: 150, angle: 0); - var splashArea = skill.GetSplashArea(SplashType.Circle, splashParam); - - Send.ZC_SKILL_READY(caster, skill, originPos, farPos); - Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); - - CallSafe(this.Attack(skill, caster, splashArea)); - } - - /// - /// Executes the actual attack after a delay. - /// - /// - /// - /// - private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) - { - var damageDelay = TimeSpan.FromMilliseconds(50); - var skillHitDelay = TimeSpan.Zero; - - var delayBetweenHits = new[] - { - TimeSpan.FromMilliseconds(50), - TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(500), - TimeSpan.FromMilliseconds(50), - TimeSpan.FromMilliseconds(700), - TimeSpan.FromMilliseconds(100), - TimeSpan.FromMilliseconds(50), - TimeSpan.FromMilliseconds(50) - }; - - // first hit hits instantly - - var hits = new List(); - - var stunChance = 0f; - if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw16, out int level)) - stunChance = level; - - var iceVariant = caster.IsAbilityActive(AbilityId.Outlaw20); - - // Caster avoids all attacks during the skill's animation - caster.StartBuff(BuffId.Skill_MomentaryEvasion_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1700), caster); - - // Outlaw17 gives a buff that prevents removable debuffs - if (caster.IsAbilityActive(AbilityId.Outlaw17)) - caster.StartBuff(BuffId.Rampage_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1700), caster); - - // Outlaw18 gives a buff that adds 10% bonus crit chance per debuff currently active - if (caster.IsAbilityActive(AbilityId.Outlaw18)) - { - int debuffCount = 0; - foreach (var buff in caster.Components.Get().GetList()) - { - if (buff.Data.Type == BuffType.Debuff) - { - debuffCount++; - } - } - - if (debuffCount > 0) - caster.StartBuff(BuffId.Rampage_Outlaw18_Buff, debuffCount, 0, TimeSpan.FromMilliseconds(1700), caster); - } - - for (var i = 0; i < 9; i++) - { - var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.Default; - - // Targets with certain statuses take 2 hits at 30% less damage - if (GetDoubleHit(target)) - { - modifier.HitCount = 2; - modifier.FinalDamageMultiplier -= 0.3f; - } - - if (iceVariant) - modifier.AttackAttribute = AttributeType.Ice; - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - hits.Add(skillHit); - - var buffRemoveChance = BuffRemoveChancePerLevel * skill.Level; - if (RandomProvider.Get().Next(1000) < buffRemoveChance) - { - target.RemoveRandomBuff(); - } - - if (RandomProvider.Get().Next(100) < stunChance) - { - target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(2), caster); - } - - // Ice effect is only applied on last hit - if (i == 8 && iceVariant) - { - // TODO: this ability states it can knock down instead - target.StartBuff(BuffId.Freeze, skill.Level, 0, TimeSpan.FromSeconds(2), caster); - } - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - - hits.Clear(); - - if (i < 8) - await Task.Delay(delayBetweenHits[i]); - } - - caster.StartBuff(BuffId.Rampage_After_Buff, skill.Level, 0, TimeSpan.FromSeconds(5), caster); - } - - - /// - /// Checks if a target takes 2 hits - /// This occurs if they have any of Blind, Bleed, or Stun - /// - /// - /// - private bool GetDoubleHit(ICombatEntity target) - { - return target.IsBuffActive(BuffId.SprinkleSands_Debuff) || target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff) || target.IsBuffActive(BuffId.Stun); - } - } -} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs deleted file mode 100644 index f8f19f2bf..000000000 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_BreakBrick.cs +++ /dev/null @@ -1,233 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Melia.Shared.Data.Database; -using Melia.Shared.Game.Const; -using Melia.Shared.L10N; -using Melia.Shared.World; -using Melia.Zone.Network; -using Melia.Zone.Skills.Combat; -using Melia.Zone.Skills.Handlers.Base; -using Melia.Zone.Skills.SplashAreas; -using Melia.Zone.World.Actors; -using Yggdrasil.Util; -using static Melia.Shared.Util.TaskHelper; -using static Melia.Zone.Skills.SkillUseFunctions; - -namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Assassin skill Brick Smash - /// - [SkillHandler(SkillId.OutLaw_BreakBrick)] - public class OutLaw_BreakBrick : IGroundSkillHandler - { - public const float JumpDistance = 60f; - - /// - /// Handles skill - /// - /// - /// - /// - /// - /// - public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) - { - if (!caster.TrySpendSp(skill)) - { - caster.ServerMessage(Localization.Get("Not enough SP.")); - return; - } - - skill.IncreaseOverheat(); - caster.SetAttackState(true); - - var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 90, width: 30, angle: 0); - var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); - - Send.ZC_SKILL_READY(caster, skill, originPos, farPos); - Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); - - // Outlaw26 is a totally different attack - if (caster.IsAbilityActive(AbilityId.Outlaw26)) - { - // This should set the overheat max to 1 - - CallSafe(this.ShatterAttack(skill, caster, farPos)); - } - else - { - CallSafe(this.Attack(skill, caster, splashArea, farPos)); - } - } - - /// - /// Executes the actual attack after a delay. - /// - /// - /// - /// - private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea, Position farPos) - { - var damageDelay = TimeSpan.FromMilliseconds(50); - var skillHitDelay = TimeSpan.Zero; - var hitDelay = TimeSpan.FromMilliseconds(400); - - // First perform the jump - var targetPos = caster.Position.GetRelative(caster.Direction, JumpDistance); - targetPos = caster.Map.Ground.GetLastValidPosition(caster.Position, targetPos); - - caster.Position = targetPos; - Send.ZC_NORMAL.LeapJump(caster, targetPos, 0.15f, 0.1f, 1f, 0.2f, 1f, 3); - - // Outlaw4 has a 20% chance to activate. If it does, it guarantees - // the stun and makes the attack a forced critical - var outlaw4Activates = caster.IsAbilityActive(AbilityId.Outlaw4) && RandomProvider.Get().Next(5) == 1; - - await Task.Delay(hitDelay); - - var hits = new List(); - var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(6); - - // 30% chance to Stun as base, 100% when behind target - var stunChance = 3; - - if (caster.IsBehind(target)) - stunChance = 10; - - if (outlaw4Activates) - { - stunChance = 10; - modifier.ForcedCritical = true; - } - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - - hits.Add(skillHit); - - if (RandomProvider.Get().Next(10) <= stunChance) - target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(3), caster); - - target.StartBuff(BuffId.BreakBrick_Debuff, skill.Level, 0, TimeSpan.FromSeconds(20), caster); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - } - - /// - /// Executes the alternate attack for Outlaw26 - /// It doesn't inflict status but has a bigger range - /// and does 3 times as many hits - /// - /// - /// - /// - private async Task ShatterAttack(Skill skill, ICombatEntity caster, Position farPos) - { - var damageDelay = TimeSpan.FromMilliseconds(50); - var skillHitDelay = TimeSpan.Zero; - var hitDelay = TimeSpan.FromMilliseconds(250); - - // This version has no jump - - // Outlaw4 has a 20% chance to activate. If it does, it inflicts - // stun and forces a critical - var outlaw4Activates = caster.IsAbilityActive(AbilityId.Outlaw4) && RandomProvider.Get().Next(5) == 1; - - await Task.Delay(hitDelay); - - // This attack uses 3 hitboxes of different shapes, which all hit simultaneously - - var splashParam = skill.GetSplashParameters(caster, caster.Position, farPos, length: 135, width: 30, angle: 0); - var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); - - var hits = new List(); - var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(6); - - // Outlaw4 has a 20% chance to activate. If it does, it causes - // a stun and makes the attack a forced critical - if (outlaw4Activates) - { - target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(3), caster); - modifier.ForcedCritical = true; - } - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - - hits.Add(skillHit); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - hits.Clear(); - - // The second hitbox is identical to the first - - targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(6); - - if (outlaw4Activates) - { - modifier.ForcedCritical = true; - } - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - - hits.Add(skillHit); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - hits.Clear(); - - // The third hitbox is a fan - - splashParam = skill.GetSplashParameters(caster, caster.Position, farPos, length: 150, width: 100, angle: 78); - splashArea = skill.GetSplashArea(SplashType.Fan, splashParam); - - targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(6); - - if (outlaw4Activates) - { - modifier.ForcedCritical = true; - } - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - - hits.Add(skillHit); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - } - } -} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs deleted file mode 100644 index b46bf2c23..000000000 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/OutLaw_SprinkleSands.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Melia.Shared.Data.Database; -using Melia.Shared.Game.Const; -using Melia.Shared.L10N; -using Melia.Shared.World; -using Melia.Zone.Network; -using Melia.Zone.Skills.Combat; -using Melia.Zone.Skills.Handlers.Base; -using Melia.Zone.Skills.SplashAreas; -using Melia.Zone.World.Actors; -using Yggdrasil.Util; -using static Melia.Shared.Util.TaskHelper; -using static Melia.Zone.Skills.SkillUseFunctions; - -namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Assassin skill Throw Sand - /// - [SkillHandler(SkillId.OutLaw_SprinkleSands)] - public class OutLaw_SprinkleSands : IGroundSkillHandler - { - /// - /// Handles skill - /// - /// - /// - /// - /// - /// - public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) - { - if (!caster.TrySpendSp(skill)) - { - caster.ServerMessage(Localization.Get("Not enough SP.")); - return; - } - - skill.IncreaseOverheat(); - caster.SetAttackState(true); - - var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 100, width: 40, angle: 0); - var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); - - Send.ZC_SKILL_READY(caster, skill, originPos, farPos); - Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); - - CallSafe(this.Attack(skill, caster, splashArea, farPos)); - } - - /// - /// Executes the actual attack after a delay. - /// - /// - /// - /// - private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea, Position farPos) - { - var damageDelay = TimeSpan.FromMilliseconds(50); - var skillHitDelay = TimeSpan.Zero; - var hitDelay = TimeSpan.FromMilliseconds(250); - - await Task.Delay(hitDelay); - - var hits = new List(); - var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - var hitTargets = targets.LimitBySDR(caster, skill); - - // Outlaw2 makes the max target count 17 - if (caster.IsAbilityActive(AbilityId.Outlaw2)) - { - hitTargets = targets.LimitRandom(17); - } - - foreach (var target in hitTargets) - { - var modifier = SkillModifier.MultiHit(4); - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - - hits.Add(skillHit); - - target.StartBuff(BuffId.Common_Silence, skill.Level, 0, TimeSpan.FromMilliseconds(200 * skill.Level), caster); - target.StartBuff(BuffId.SprinkleSands_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); - - target.StartBuff(BuffId.DecreaseHeal_Debuff, skill.Level, this.GetHealingReduction(skill), TimeSpan.FromSeconds(5), caster); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - } - - /// - /// Return the Healing Reduction value - /// - /// - /// - private float GetHealingReduction(Skill skill) - { - return (2.3f * skill.Level) * 1000; - } - } -} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs b/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs deleted file mode 100644 index c967a4e97..000000000 --- a/src/ZoneServer/Skills/Handlers/Scouts/Outlaw/Outlaw_Mangle.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using Melia.Shared.Data.Database; -using Melia.Shared.Game.Const; -using Melia.Shared.L10N; -using Melia.Shared.World; -using Melia.Zone.Network; -using Melia.Zone.Skills.Combat; -using Melia.Zone.Skills.Handlers.Base; -using Melia.Zone.Skills.SplashAreas; -using Melia.Zone.World.Actors; -using static Melia.Shared.Util.TaskHelper; -using static Melia.Zone.Skills.SkillUseFunctions; - -namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Outlaw skill Mangle. - /// - [SkillHandler(SkillId.OutLaw_Mangle)] - public class OutLaw_Mangle : IGroundSkillHandler - { - /// - /// Handles skill, damaging targets. - /// - /// - /// - /// - /// - public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) - { - if (!caster.TrySpendSp(skill)) - { - caster.ServerMessage(Localization.Get("Not enough SP.")); - return; - } - - skill.IncreaseOverheat(); - caster.SetAttackState(true); - - var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 40, width: 20, angle: 0); - var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); - - Send.ZC_SKILL_READY(caster, skill, originPos, farPos); - Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); - - CallSafe(this.Attack(skill, caster, splashArea)); - } - - /// - /// Executes the actual attack after a delay. - /// - /// - /// - /// - private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) - { - var hitDelay1 = TimeSpan.FromMilliseconds(350); - var hitDelay2 = TimeSpan.FromMilliseconds(600); - var hitDelay3 = TimeSpan.FromMilliseconds(50); - var damageDelay = TimeSpan.FromMilliseconds(50); - var skillHitDelay = TimeSpan.Zero; - - // Outlaw6 gives guaranteed evade throughout the whole animation - if (caster.IsAbilityActive(AbilityId.Outlaw6)) - caster.StartBuff(BuffId.Mangle_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1300), caster); - - // First attack hits with no delay - - var hits = new List(); - var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(3); - modifier.FinalDamageMultiplier += GetDamageBonus(target); - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - hits.Add(skillHit); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - hits.Clear(); - await Task.Delay(hitDelay1); - - targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(2); - - // Outlaw7 adds 3 more hits - if (caster.IsAbilityActive(AbilityId.Outlaw7)) - modifier.HitCount += 3; - - modifier.FinalDamageMultiplier += GetDamageBonus(target); - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - hits.Add(skillHit); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - hits.Clear(); - await Task.Delay(hitDelay2); - - targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.Default; - modifier.FinalDamageMultiplier += GetDamageBonus(target); - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - hits.Add(skillHit); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - hits.Clear(); - await Task.Delay(hitDelay3); - - targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); - - foreach (var target in targets.LimitBySDR(caster, skill)) - { - var modifier = SkillModifier.MultiHit(3); - modifier.FinalDamageMultiplier += GetDamageBonus(target); - - var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); - target.TakeDamage(skillHitResult.Damage, caster); - - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); - skillHit.HitEffect = HitEffect.Impact; - hits.Add(skillHit); - - target.StartBuff(BuffId.MangleAndFireBlindly_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); - } - - Send.ZC_SKILL_HIT_INFO(caster, hits); - } - - - /// - /// Return the bonus damage - /// Adds 0.5 to final damage for each of Blind, Bleed, or Stun - /// - /// - /// - private float GetDamageBonus(ICombatEntity target) - { - float damageBonus = 0f; - - if (target.IsBuffActive(BuffId.SprinkleSands_Debuff)) - damageBonus += 0.5f; - - if (target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff)) - damageBonus += 0.5f; - - if (target.IsBuffActive(BuffId.Stun)) - damageBonus += 0.5f; - - return damageBonus; - } - } -} From d1ab126dfabc4d067cf509c2c0a76e7ff876bff5 Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:47:11 -0500 Subject: [PATCH 6/8] Readding files --- .../Handlers/Scouts/OutLaw/OutLaw_Aggress.cs | 82 ++++++ .../Scouts/OutLaw/OutLaw_BreakBrick.cs | 233 ++++++++++++++++++ .../Handlers/Scouts/OutLaw/OutLaw_Bully.cs | 49 ++++ .../Scouts/OutLaw/OutLaw_FireBlindly.cs | 135 ++++++++++ .../Handlers/Scouts/OutLaw/OutLaw_Rampage.cs | 179 ++++++++++++++ .../Scouts/OutLaw/OutLaw_SprinkleSands.cs | 109 ++++++++ .../Handlers/Scouts/OutLaw/Outlaw_Mangle.cs | 177 +++++++++++++ 7 files changed, 964 insertions(+) create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_BreakBrick.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_SprinkleSands.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/OutLaw/Outlaw_Mangle.cs diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs new file mode 100644 index 000000000..955d75c69 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Aggress.cs @@ -0,0 +1,82 @@ +using System; +using Melia.Shared.L10N; +using Melia.Shared.Game.Const; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Melia.Shared.Data.Database; +using Melia.Zone.Scripting.AI; +using Melia.Zone.Skills.SplashAreas; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Aggress. + /// + [SkillHandler(SkillId.OutLaw_Aggress)] + public class OutLaw_Aggress : IGroundSkillHandler + { + /// + /// Handles the skill, debuffing enemies in the target area. + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + Send.ZC_SKILL_MELEE_GROUND(caster, skill, caster.Position, null); + + var duration = TimeSpan.FromSeconds(10); + + // Splash area is a square of length 300, width 100, starting 100 + // units behind the caster + var splashArea = new Square(caster.Position.GetRelative(caster.Direction, -100f), caster.Direction, 200f, 100f); + Debug.ShowShape(caster.Map, splashArea); + + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + var maxTargets = Math.Min(targets.Count, 3 * skill.Level); + + var unhingeLevel = 0f; + + if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw13, out var level)) + unhingeLevel = level; + + for (var i = 0; i < maxTargets; i++) + { + var target = targets[i]; + if (target.IsBuffActive(BuffId.ProvocationImmunity_Debuff)) + continue; + + target.StartBuff(BuffId.Aggress_Debuff, skill.Level, unhingeLevel, duration, caster); + target.StartBuff(BuffId.ProvocationImmunity_Debuff, skill.Level, 0, duration, caster); + + if (target.Components.TryGet(out var component)) + { + // Reset hate and simulate a hit to build a small amount + // of threat + component.Script.QueueEventAlert(new HateResetAlert(caster)); + component.Script.QueueEventAlert(new HitEventAlert(target, caster, 0)); + } + } + + // Outlaw14 adds a massive speed buff + if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw14, out var getawayLevel)) + { + caster.StartBuff(BuffId.Aggress_Buff, skill.Level, 0, TimeSpan.FromSeconds(getawayLevel), caster); + } + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_BreakBrick.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_BreakBrick.cs new file mode 100644 index 000000000..f8f19f2bf --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_BreakBrick.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using Yggdrasil.Util; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Assassin skill Brick Smash + /// + [SkillHandler(SkillId.OutLaw_BreakBrick)] + public class OutLaw_BreakBrick : IGroundSkillHandler + { + public const float JumpDistance = 60f; + + /// + /// Handles skill + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 90, width: 30, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + // Outlaw26 is a totally different attack + if (caster.IsAbilityActive(AbilityId.Outlaw26)) + { + // This should set the overheat max to 1 + + CallSafe(this.ShatterAttack(skill, caster, farPos)); + } + else + { + CallSafe(this.Attack(skill, caster, splashArea, farPos)); + } + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea, Position farPos) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + var hitDelay = TimeSpan.FromMilliseconds(400); + + // First perform the jump + var targetPos = caster.Position.GetRelative(caster.Direction, JumpDistance); + targetPos = caster.Map.Ground.GetLastValidPosition(caster.Position, targetPos); + + caster.Position = targetPos; + Send.ZC_NORMAL.LeapJump(caster, targetPos, 0.15f, 0.1f, 1f, 0.2f, 1f, 3); + + // Outlaw4 has a 20% chance to activate. If it does, it guarantees + // the stun and makes the attack a forced critical + var outlaw4Activates = caster.IsAbilityActive(AbilityId.Outlaw4) && RandomProvider.Get().Next(5) == 1; + + await Task.Delay(hitDelay); + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + // 30% chance to Stun as base, 100% when behind target + var stunChance = 3; + + if (caster.IsBehind(target)) + stunChance = 10; + + if (outlaw4Activates) + { + stunChance = 10; + modifier.ForcedCritical = true; + } + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + + if (RandomProvider.Get().Next(10) <= stunChance) + target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(3), caster); + + target.StartBuff(BuffId.BreakBrick_Debuff, skill.Level, 0, TimeSpan.FromSeconds(20), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + + /// + /// Executes the alternate attack for Outlaw26 + /// It doesn't inflict status but has a bigger range + /// and does 3 times as many hits + /// + /// + /// + /// + private async Task ShatterAttack(Skill skill, ICombatEntity caster, Position farPos) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + var hitDelay = TimeSpan.FromMilliseconds(250); + + // This version has no jump + + // Outlaw4 has a 20% chance to activate. If it does, it inflicts + // stun and forces a critical + var outlaw4Activates = caster.IsAbilityActive(AbilityId.Outlaw4) && RandomProvider.Get().Next(5) == 1; + + await Task.Delay(hitDelay); + + // This attack uses 3 hitboxes of different shapes, which all hit simultaneously + + var splashParam = skill.GetSplashParameters(caster, caster.Position, farPos, length: 135, width: 30, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + // Outlaw4 has a 20% chance to activate. If it does, it causes + // a stun and makes the attack a forced critical + if (outlaw4Activates) + { + target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(3), caster); + modifier.ForcedCritical = true; + } + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + + // The second hitbox is identical to the first + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + if (outlaw4Activates) + { + modifier.ForcedCritical = true; + } + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + + // The third hitbox is a fan + + splashParam = skill.GetSplashParameters(caster, caster.Position, farPos, length: 150, width: 100, angle: 78); + splashArea = skill.GetSplashArea(SplashType.Fan, splashParam); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(6); + + if (outlaw4Activates) + { + modifier.ForcedCritical = true; + } + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs new file mode 100644 index 000000000..6cfb455c7 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Bully.cs @@ -0,0 +1,49 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Bully. + /// + [SkillHandler(SkillId.OutLaw_Bully)] + public class OutLaw_Bully : ISelfSkillHandler + { + /// + /// Handles skill, applying a buff to the caster. + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Direction dir) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + // Uses a totally different buff if Outlaw19 is active + if (caster.IsAbilityActive(AbilityId.Outlaw19)) + { + caster.StartBuff(BuffId.BullyPainBarrier_Buff, skill.Level, 0, TimeSpan.FromSeconds(20), caster); + } + else + { + caster.StartBuff(BuffId.Bully_Buff, skill.Level, 0, TimeSpan.FromSeconds(60), caster); + } + + Send.ZC_SKILL_MELEE_TARGET(caster, skill, caster, null); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs new file mode 100644 index 000000000..fba7199c9 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_FireBlindly.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Blindfire + /// + [SkillHandler(SkillId.OutLaw_FireBlindly)] + public class OutLaw_FireBlindly : IGroundSkillHandler + { + /// + /// Handles skill, damaging targets. + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 120, width: 40, angle: 80); + var splashArea = skill.GetSplashArea(SplashType.Fan, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) + { + var hitDelay = TimeSpan.FromMilliseconds(360); + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + + // Outlaw9 gives guaranteed evade throughout the whole animation + if (caster.IsAbilityActive(AbilityId.Outlaw9)) + caster.StartBuff(BuffId.FireBlindly_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(600), caster); + + // First attack hits with no delay + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(4); + modifier.BonusCritChance += GetCritBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + + // Outlaw10 adds 30% damage on a critical + if (caster.IsAbilityActive(AbilityId.Outlaw10) && skillHitResult.Result == HitResultType.Crit) + skillHitResult.Damage *= 1.3f; + + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(4); + modifier.BonusCritChance += GetCritBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + + // Outlaw10 adds 30% damage on a critical + if (caster.IsAbilityActive(AbilityId.Outlaw10) && skillHitResult.Result == HitResultType.Crit) + skillHitResult.Damage *= 1.3f; + + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + + // Description doesn't mention this, but it's in skill_bytool + target.StartBuff(BuffId.MangleAndFireBlindly_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + + + /// + /// Return the bonus damage + /// Adds 20 to crit chance if target has any of Blind, Bleed, or Stun + /// + /// + /// + private float GetCritBonus(ICombatEntity target) + { + if (target.IsBuffActive(BuffId.SprinkleSands_Debuff) || target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff) || target.IsBuffActive(BuffId.Stun)) + return 20f; + + return 0f; + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs new file mode 100644 index 000000000..6a3b075f7 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_Rampage.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Yggdrasil.Logging; +using Yggdrasil.Util; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Rampage. + /// + [SkillHandler(SkillId.OutLaw_Rampage)] + public class OutLaw_Rampage : IGroundSkillHandler + { + private const float BuffRemoveChancePerLevel = 3f; + + /// + /// Handles skill, damaging targets. + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 0, width: 150, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Circle, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + + var delayBetweenHits = new[] + { + TimeSpan.FromMilliseconds(50), + TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(500), + TimeSpan.FromMilliseconds(50), + TimeSpan.FromMilliseconds(700), + TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(50), + TimeSpan.FromMilliseconds(50) + }; + + // first hit hits instantly + + var hits = new List(); + + var stunChance = 0f; + if (caster.TryGetActiveAbilityLevel(AbilityId.Outlaw16, out int level)) + stunChance = level; + + var iceVariant = caster.IsAbilityActive(AbilityId.Outlaw20); + + // Caster avoids all attacks during the skill's animation + caster.StartBuff(BuffId.Skill_MomentaryEvasion_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1700), caster); + + // Outlaw17 gives a buff that prevents removable debuffs + if (caster.IsAbilityActive(AbilityId.Outlaw17)) + caster.StartBuff(BuffId.Rampage_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1700), caster); + + // Outlaw18 gives a buff that adds 10% bonus crit chance per debuff currently active + if (caster.IsAbilityActive(AbilityId.Outlaw18)) + { + int debuffCount = 0; + foreach (var buff in caster.Components.Get().GetList()) + { + if (buff.Data.Type == BuffType.Debuff) + { + debuffCount++; + } + } + + if (debuffCount > 0) + caster.StartBuff(BuffId.Rampage_Outlaw18_Buff, debuffCount, 0, TimeSpan.FromMilliseconds(1700), caster); + } + + for (var i = 0; i < 9; i++) + { + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.Default; + + // Targets with certain statuses take 2 hits at 30% less damage + if (GetDoubleHit(target)) + { + modifier.HitCount = 2; + modifier.FinalDamageMultiplier -= 0.3f; + } + + if (iceVariant) + modifier.AttackAttribute = AttributeType.Ice; + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + + var buffRemoveChance = BuffRemoveChancePerLevel * skill.Level; + if (RandomProvider.Get().Next(1000) < buffRemoveChance) + { + target.RemoveRandomBuff(); + } + + if (RandomProvider.Get().Next(100) < stunChance) + { + target.StartBuff(BuffId.Stun, skill.Level, 0, TimeSpan.FromSeconds(2), caster); + } + + // Ice effect is only applied on last hit + if (i == 8 && iceVariant) + { + // TODO: this ability states it can knock down instead + target.StartBuff(BuffId.Freeze, skill.Level, 0, TimeSpan.FromSeconds(2), caster); + } + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + + hits.Clear(); + + if (i < 8) + await Task.Delay(delayBetweenHits[i]); + } + + caster.StartBuff(BuffId.Rampage_After_Buff, skill.Level, 0, TimeSpan.FromSeconds(5), caster); + } + + + /// + /// Checks if a target takes 2 hits + /// This occurs if they have any of Blind, Bleed, or Stun + /// + /// + /// + private bool GetDoubleHit(ICombatEntity target) + { + return target.IsBuffActive(BuffId.SprinkleSands_Debuff) || target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff) || target.IsBuffActive(BuffId.Stun); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_SprinkleSands.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_SprinkleSands.cs new file mode 100644 index 000000000..b46bf2c23 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/OutLaw_SprinkleSands.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using Yggdrasil.Util; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Assassin skill Throw Sand + /// + [SkillHandler(SkillId.OutLaw_SprinkleSands)] + public class OutLaw_SprinkleSands : IGroundSkillHandler + { + /// + /// Handles skill + /// + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity designatedTarget) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 100, width: 40, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea, farPos)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea, Position farPos) + { + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + var hitDelay = TimeSpan.FromMilliseconds(250); + + await Task.Delay(hitDelay); + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + var hitTargets = targets.LimitBySDR(caster, skill); + + // Outlaw2 makes the max target count 17 + if (caster.IsAbilityActive(AbilityId.Outlaw2)) + { + hitTargets = targets.LimitRandom(17); + } + + foreach (var target in hitTargets) + { + var modifier = SkillModifier.MultiHit(4); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + + hits.Add(skillHit); + + target.StartBuff(BuffId.Common_Silence, skill.Level, 0, TimeSpan.FromMilliseconds(200 * skill.Level), caster); + target.StartBuff(BuffId.SprinkleSands_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); + + target.StartBuff(BuffId.DecreaseHeal_Debuff, skill.Level, this.GetHealingReduction(skill), TimeSpan.FromSeconds(5), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + + /// + /// Return the Healing Reduction value + /// + /// + /// + private float GetHealingReduction(Skill skill) + { + return (2.3f * skill.Level) * 1000; + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/Outlaw_Mangle.cs b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/Outlaw_Mangle.cs new file mode 100644 index 000000000..c967a4e97 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/OutLaw/Outlaw_Mangle.cs @@ -0,0 +1,177 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Shared.L10N; +using Melia.Shared.World; +using Melia.Zone.Network; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.Skills.SplashAreas; +using Melia.Zone.World.Actors; +using static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Outlaw skill Mangle. + /// + [SkillHandler(SkillId.OutLaw_Mangle)] + public class OutLaw_Mangle : IGroundSkillHandler + { + /// + /// Handles skill, damaging targets. + /// + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + skill.IncreaseOverheat(); + caster.SetAttackState(true); + + var splashParam = skill.GetSplashParameters(caster, originPos, farPos, length: 40, width: 20, angle: 0); + var splashArea = skill.GetSplashArea(SplashType.Square, splashParam); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + + CallSafe(this.Attack(skill, caster, splashArea)); + } + + /// + /// Executes the actual attack after a delay. + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster, ISplashArea splashArea) + { + var hitDelay1 = TimeSpan.FromMilliseconds(350); + var hitDelay2 = TimeSpan.FromMilliseconds(600); + var hitDelay3 = TimeSpan.FromMilliseconds(50); + var damageDelay = TimeSpan.FromMilliseconds(50); + var skillHitDelay = TimeSpan.Zero; + + // Outlaw6 gives guaranteed evade throughout the whole animation + if (caster.IsAbilityActive(AbilityId.Outlaw6)) + caster.StartBuff(BuffId.Mangle_Buff, skill.Level, 0, TimeSpan.FromMilliseconds(1300), caster); + + // First attack hits with no delay + + var hits = new List(); + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(3); + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay1); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(2); + + // Outlaw7 adds 3 more hits + if (caster.IsAbilityActive(AbilityId.Outlaw7)) + modifier.HitCount += 3; + + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay2); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.Default; + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + hits.Clear(); + await Task.Delay(hitDelay3); + + targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + var modifier = SkillModifier.MultiHit(3); + modifier.FinalDamageMultiplier += GetDamageBonus(target); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHit.HitEffect = HitEffect.Impact; + hits.Add(skillHit); + + target.StartBuff(BuffId.MangleAndFireBlindly_Debuff, skill.Level, 0, TimeSpan.FromSeconds(10), caster); + } + + Send.ZC_SKILL_HIT_INFO(caster, hits); + } + + + /// + /// Return the bonus damage + /// Adds 0.5 to final damage for each of Blind, Bleed, or Stun + /// + /// + /// + private float GetDamageBonus(ICombatEntity target) + { + float damageBonus = 0f; + + if (target.IsBuffActive(BuffId.SprinkleSands_Debuff)) + damageBonus += 0.5f; + + if (target.IsBuffActive(BuffId.HeavyBleeding) || target.IsBuffActive(BuffId.Behead_Debuff)) + damageBonus += 0.5f; + + if (target.IsBuffActive(BuffId.Stun)) + damageBonus += 0.5f; + + return damageBonus; + } + } +} From 03b9fd9288e63b21dbc51848375c23fe50b739b5 Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:47:45 -0500 Subject: [PATCH 7/8] Cleaning up folders --- .../Handlers/Scouts/OutLaw/Aggress_Buff.cs | 37 --------- .../Handlers/Scouts/OutLaw/Aggress_Debuff.cs | 64 --------------- .../Scouts/OutLaw/BullyPainBarrier_Buff.cs | 77 ------------------- .../Handlers/Scouts/OutLaw/Bully_Buff.cs | 76 ------------------ .../Scouts/OutLaw/FireBlindly_Buff.cs | 29 ------- .../Scouts/OutLaw/Rampage_After_Buff.cs | 51 ------------ .../Scouts/OutLaw/Rampage_Outlaw18_Buff.cs | 36 --------- .../Scouts/Outlaw/BreakBrick_Debuff.cs | 43 ----------- .../Outlaw/MangleAndFireBlindly_Debuff.cs | 37 --------- .../Handlers/Scouts/Outlaw/Mangle_Buff.cs | 29 ------- .../Scouts/Outlaw/SprinkleSands_Debuff.cs | 42 ---------- 11 files changed, 521 deletions(-) delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs delete mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs deleted file mode 100644 index 9c063e2e1..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Network; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handler for Aggress Buff, which massively increases movement speed - /// - [BuffHandler(BuffId.Aggress_Buff)] - public class Aggress_Buff : BuffHandler - { - private const float MspdBonus = 30f; - - /// - /// Starts buff, modifying the movement speed - /// - /// - public override void OnActivate(Buff buff, ActivationType activationType) - { - var target = buff.Target; - - AddPropertyModifier(buff, target, PropertyName.MSPD_BM, MspdBonus); - Send.ZC_MSPD(target); - } - - /// - /// Ends the buff, resetting the movement speed - /// - /// - public override void OnEnd(Buff buff) - { - RemovePropertyModifier(buff, buff.Target, PropertyName.MSPD_BM); - Send.ZC_MSPD(buff.Target); - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs deleted file mode 100644 index 1bae39d3c..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Network; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handler for Break Brick Debuff, which reduces Evasion - /// - /// - /// NumArg1: Skill Level - /// NumArg2: Unhinge Level - /// This is related to Outlaw13, which changes the buff effect to - /// also increase movement speed but decrease accuracy - /// - [BuffHandler(BuffId.Aggress_Debuff)] - internal class Aggress_Debuff : BuffHandler - { - private const float DRPenaltyPerLevel = 0.02f; - private const float HRPenaltyPerLevel = 0.03f; - private const float MspdBonus = 5f; - - /// - /// Starts buff, reducing Dodge Rate - /// - /// - public override void OnActivate(Buff buff, ActivationType activationType) - { - var reduceDR = buff.Target.Properties.GetFloat(PropertyName.DR) * buff.NumArg1 * DRPenaltyPerLevel; - - AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, -reduceDR); - - if (buff.NumArg2 > 0) - { - var reduceHR = buff.Target.Properties.GetFloat(PropertyName.HR) * buff.NumArg2 * HRPenaltyPerLevel; - - AddPropertyModifier(buff, buff.Target, PropertyName.HR_BM, -reduceHR); - AddPropertyModifier(buff, buff.Target, PropertyName.MSPD_BM, MspdBonus); - Send.ZC_MSPD(buff.Target); - } - } - - /// - /// Ends the buff, resetting dodge rate. - /// - /// - public override void OnEnd(Buff buff) - { - RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); - - if (buff.NumArg2 > 0) - { - RemovePropertyModifier(buff, buff.Target, PropertyName.HR_BM); - RemovePropertyModifier(buff, buff.Target, PropertyName.MSPD_BM); - Send.ZC_MSPD(buff.Target); - } - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs deleted file mode 100644 index 39af8e442..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Scripting.AI; -using Melia.Zone.Skills; -using Melia.Zone.Skills.Combat; -using Melia.Zone.World.Actors; -using Melia.Zone.World.Actors.CombatEntities.Components; -using Melia.Zone.World.Actors.Monsters; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Buff handler for Bully Pain Barrier Buff, which increases evasion - /// and adds threat on successful evade - /// It's completely identical to Bully Buff except that it also - /// grants immunity to knockback and Outlaw12's effect is nerfed - /// - /// - /// NumArg1: Skill Level - /// NumArg2: None - /// - [BuffHandler(BuffId.BullyPainBarrier_Buff)] - public class BullyPainBarrier_Buff : BuffHandler, IBuffCombatDefenseAfterCalcHandler - { - private const float DrBuffRateBase = 0.24f; - private const float DrBuffRatePerLevel = 0.04f; - private const float HatePerLevel = 3f; - - /// - /// Starts buff, increasing dodge rate. - /// - /// - public override void OnActivate(Buff buff, ActivationType activationType) - { - var dr = buff.Target.Properties.GetFloat(PropertyName.DR); - var skillLevel = buff.NumArg1; - var rate = DrBuffRateBase + DrBuffRatePerLevel * skillLevel; - var bonus = dr * rate; - - AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, bonus); - } - - /// - /// Ends the buff, resetting dodge rate. - /// - /// - public override void OnEnd(Buff buff) - { - RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); - } - - /// - /// Applies the buff's effect during the combat calculations. - /// - /// - /// - /// - /// - /// - /// - public void OnDefenseAfterCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - if (skillHitResult.Result == HitResultType.Dodge && attacker.Components.TryGet(out var component)) - { - component.Script.QueueEventAlert(new HateIncreaseAlert(target, buff.NumArg1 * HatePerLevel)); - - // Outlaw12 adds additional duration to the buff on successful evade - // For Pain Barrier buff, the maximum increase is 2 seconds - if (target.TryGetActiveAbilityLevel(AbilityId.Outlaw12, out var level)) - { - buff.IncreaseDuration(TimeSpan.FromSeconds(Math.Max(level, 2))); - } - } - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs deleted file mode 100644 index 21e1dd388..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Scripting.AI; -using Melia.Zone.Skills; -using Melia.Zone.Skills.Combat; -using Melia.Zone.World.Actors; -using Melia.Zone.World.Actors.CombatEntities.Components; -using Melia.Zone.World.Actors.Monsters; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Buff handler for Bully Buff, which increases evasion - /// and adds threat on successful evade - /// - /// - /// NumArg1: Skill Level - /// NumArg2: None - /// - [BuffHandler(BuffId.Bully_Buff)] - public class Bully_Buff : BuffHandler, IBuffCombatDefenseAfterCalcHandler - { - private const float DrBuffRateBase = 0.24f; - private const float DrBuffRatePerLevel = 0.04f; - private const float HatePerLevel = 3f; - - /// - /// Starts buff, increasing dodge rate. - /// - /// - public override void OnActivate(Buff buff, ActivationType activationType) - { - var dr = buff.Target.Properties.GetFloat(PropertyName.DR); - var skillLevel = buff.NumArg1; - var rate = DrBuffRateBase + DrBuffRatePerLevel * skillLevel; - var bonus = dr * rate; - - AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, bonus); - } - - /// - /// Ends the buff, resetting dodge rate. - /// - /// - public override void OnEnd(Buff buff) - { - RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); - } - - /// - /// Applies the buff's effect during the combat calculations. - /// - /// - /// - /// - /// - /// - /// - public void OnDefenseAfterCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - if (skillHitResult.Result == HitResultType.Dodge && attacker.Components.TryGet(out var component)) - { - component.Script.QueueEventAlert(new HateIncreaseAlert(target, buff.NumArg1 * HatePerLevel)); - - // Outlaw12 adds additional duration to the buff on successful evade - // Note that it only applies to monster attacks, which is why - // it's done after the AI script check - if (target.TryGetActiveAbilityLevel(AbilityId.Outlaw12, out var level)) - { - buff.IncreaseDuration(TimeSpan.FromSeconds(level)); - } - } - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs deleted file mode 100644 index a65d9258c..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Skills; -using Melia.Zone.Skills.Combat; -using Melia.Zone.World.Actors; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Fire Blindly Buff, which grants forced evade - /// - [BuffHandler(BuffId.FireBlindly_Buff)] - public class FireBlindly_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler - { - /// - /// Applies the buff's effect during the combat calculations. - /// - /// - /// - /// - /// - /// - /// - public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - modifier.ForcedEvade = true; - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs deleted file mode 100644 index 2ca0bc548..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Skills; -using Melia.Zone.Skills.Combat; -using Melia.Zone.World.Actors; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handle for the Rampage After Buff, which increases damage - /// dealt and taken by 50% and prevents the caster from evading - /// - /// - /// NumArg1: Level - /// NumArg2: None - /// - [BuffHandler(BuffId.Rampage_After_Buff)] - public class Rampage_After_Buff : BuffHandler, IBuffCombatAttackBeforeCalcHandler, IBuffCombatDefenseBeforeCalcHandler - { - private const float DamageBonus = 0.5f; - private const float DamagePenalty = 0.5f; - - /// - /// Adds the damage bonus - /// - /// - /// - /// - /// - /// - public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - modifier.FinalDamageMultiplier += DamageBonus; - } - - /// - /// Adds the damage penalty and prevents evasion - /// - /// - /// - /// - /// - /// - /// - public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - modifier.FinalDamageMultiplier += DamagePenalty; - modifier.ForcedHit = true; - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs deleted file mode 100644 index 65fec8563..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Skills; -using Melia.Zone.Skills.Combat; -using Melia.Zone.World.Actors; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handle for the Rampage Outlaw18 Buff, which gives 10% - /// bonus crit chance per debuff that was active when it - /// was started (which was calculated when it was applied) - /// - /// - /// NumArg1: Debuff Count - /// NumArg2: None - /// - [BuffHandler(BuffId.Rampage_Outlaw18_Buff)] - public class Rampage_Outlaw18_Buff : BuffHandler, IBuffCombatAttackBeforeCalcHandler - { - public const float CritBonusPerDebuff = 10f; - - /// - /// Adds the bonus crit chance - /// - /// - /// - /// - /// - /// - public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - modifier.BonusCritChance = CritBonusPerDebuff * buff.NumArg1; - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs deleted file mode 100644 index 34948b5bb..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/BreakBrick_Debuff.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handler for Break Brick Debuff, which reduces Crit Chance - /// - /// - /// NumArg1: Skill Level - /// NumArg2: None - /// - [BuffHandler(BuffId.BreakBrick_Debuff)] - internal class BreakBrick_Debuff : BuffHandler - { - private const float CRTPenaltyPerLevel = 1f; - - /// - /// Starts buff, reducing Crit rate - /// - /// - public override void OnActivate(Buff buff, ActivationType activationType) - { - var reduceCrt = buff.Target.Properties.GetFloat(PropertyName.CRTHR) * buff.NumArg1 * CRTPenaltyPerLevel; - - AddPropertyModifier(buff, buff.Target, PropertyName.CRTHR_BM, -reduceCrt); - } - - /// - /// Ends the buff, resetting Crit rate. - /// - /// - public override void OnEnd(Buff buff) - { - RemovePropertyModifier(buff, buff.Target, PropertyName.CRTHR_BM); - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs deleted file mode 100644 index c059b2f8a..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/MangleAndFireBlindly_Debuff.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Melia.Shared.Data.Database; -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Skills; -using Melia.Zone.Skills.Combat; -using Melia.Zone.World.Actors; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handle for the Mangle and Fire Blindly Debuff, which - /// reduces damage dealt to the inflictor of the buff - /// - /// - /// NumArg1: Skill Level - /// NumArg2: Extra Crit Chance - /// - [BuffHandler(BuffId.MangleAndFireBlindly_Debuff)] - public class MangleAndFireBlindly_Debuff : BuffHandler, IBuffCombatAttackBeforeCalcHandler - { - /// - /// Reduces damage dealt if the target is the caster of the buff - /// - /// - /// - /// - /// - /// - public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - if (target == buff.Caster) - { - modifier.FinalDamageMultiplier -= 0.07f * buff.OverbuffCounter; - } - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs deleted file mode 100644 index 39399b03d..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/Mangle_Buff.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; -using Melia.Zone.Skills; -using Melia.Zone.Skills.Combat; -using Melia.Zone.World.Actors; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handler for the Mangle Buff, which grants forced evade - /// - [BuffHandler(BuffId.Mangle_Buff)] - public class Mangle_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler - { - /// - /// Applies the buff's effect during the combat calculations. - /// - /// - /// - /// - /// - /// - /// - public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) - { - modifier.ForcedEvade = true; - } - } -} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs deleted file mode 100644 index 0c2a32b79..000000000 --- a/src/ZoneServer/Buffs/Handlers/Scouts/Outlaw/SprinkleSands_Debuff.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Melia.Shared.Game.Const; -using Melia.Zone.Buffs.Base; - -namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw -{ - /// - /// Handler for Sprinkle Sands Debuff, which reduces Accuracy and Evasion - /// - /// - /// NumArg1: Skill Level - /// NumArg2: None - /// - [BuffHandler(BuffId.SprinkleSands_Debuff)] - public class SprinkleSands_Debuff : BuffHandler - { - private const float HRPenaltyRate = 0.2f; - private const float DRPenaltyRate = 0.2f; - - /// - /// Starts buff, reducing HR and DR - /// - /// - public override void OnActivate(Buff buff, ActivationType activationType) - { - var reduceHR = buff.Target.Properties.GetFloat(PropertyName.HR) * HRPenaltyRate; - var reduceDR = buff.Target.Properties.GetFloat(PropertyName.DR) * DRPenaltyRate; - - AddPropertyModifier(buff, buff.Target, PropertyName.HR_BM, -reduceHR); - AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, -reduceDR); - } - - /// - /// Ends the buff, resetting HR and DR. - /// - /// - public override void OnEnd(Buff buff) - { - RemovePropertyModifier(buff, buff.Target, PropertyName.HR_BM); - RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); - } - } -} From 12deb846dfa650f56a97f730a82144a586e6cfb7 Mon Sep 17 00:00:00 2001 From: Terotrous <139150104+Terotrous@users.noreply.github.com> Date: Sun, 3 Nov 2024 18:48:10 -0500 Subject: [PATCH 8/8] Readding files --- .../Handlers/Scouts/OutLaw/Aggress_Buff.cs | 37 +++++++++ .../Handlers/Scouts/OutLaw/Aggress_Debuff.cs | 64 +++++++++++++++ .../Scouts/OutLaw/BreakBrick_Debuff.cs | 43 +++++++++++ .../Scouts/OutLaw/BullyPainBarrier_Buff.cs | 77 +++++++++++++++++++ .../Handlers/Scouts/OutLaw/Bully_Buff.cs | 76 ++++++++++++++++++ .../Scouts/OutLaw/FireBlindly_Buff.cs | 29 +++++++ .../OutLaw/MangleAndFireBlindly_Debuff.cs | 37 +++++++++ .../Handlers/Scouts/OutLaw/Mangle_Buff.cs | 29 +++++++ .../Scouts/OutLaw/Rampage_After_Buff.cs | 51 ++++++++++++ .../Scouts/OutLaw/Rampage_Outlaw18_Buff.cs | 36 +++++++++ .../Scouts/OutLaw/SprinkleSands_Debuff.cs | 42 ++++++++++ 11 files changed, 521 insertions(+) create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BreakBrick_Debuff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/MangleAndFireBlindly_Debuff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Mangle_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/SprinkleSands_Debuff.cs diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs new file mode 100644 index 000000000..9c063e2e1 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Buff.cs @@ -0,0 +1,37 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Network; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for Aggress Buff, which massively increases movement speed + /// + [BuffHandler(BuffId.Aggress_Buff)] + public class Aggress_Buff : BuffHandler + { + private const float MspdBonus = 30f; + + /// + /// Starts buff, modifying the movement speed + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var target = buff.Target; + + AddPropertyModifier(buff, target, PropertyName.MSPD_BM, MspdBonus); + Send.ZC_MSPD(target); + } + + /// + /// Ends the buff, resetting the movement speed + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.MSPD_BM); + Send.ZC_MSPD(buff.Target); + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs new file mode 100644 index 000000000..1bae39d3c --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Aggress_Debuff.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Network; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for Break Brick Debuff, which reduces Evasion + /// + /// + /// NumArg1: Skill Level + /// NumArg2: Unhinge Level + /// This is related to Outlaw13, which changes the buff effect to + /// also increase movement speed but decrease accuracy + /// + [BuffHandler(BuffId.Aggress_Debuff)] + internal class Aggress_Debuff : BuffHandler + { + private const float DRPenaltyPerLevel = 0.02f; + private const float HRPenaltyPerLevel = 0.03f; + private const float MspdBonus = 5f; + + /// + /// Starts buff, reducing Dodge Rate + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var reduceDR = buff.Target.Properties.GetFloat(PropertyName.DR) * buff.NumArg1 * DRPenaltyPerLevel; + + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, -reduceDR); + + if (buff.NumArg2 > 0) + { + var reduceHR = buff.Target.Properties.GetFloat(PropertyName.HR) * buff.NumArg2 * HRPenaltyPerLevel; + + AddPropertyModifier(buff, buff.Target, PropertyName.HR_BM, -reduceHR); + AddPropertyModifier(buff, buff.Target, PropertyName.MSPD_BM, MspdBonus); + Send.ZC_MSPD(buff.Target); + } + } + + /// + /// Ends the buff, resetting dodge rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + + if (buff.NumArg2 > 0) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.HR_BM); + RemovePropertyModifier(buff, buff.Target, PropertyName.MSPD_BM); + Send.ZC_MSPD(buff.Target); + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BreakBrick_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BreakBrick_Debuff.cs new file mode 100644 index 000000000..34948b5bb --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BreakBrick_Debuff.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for Break Brick Debuff, which reduces Crit Chance + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.BreakBrick_Debuff)] + internal class BreakBrick_Debuff : BuffHandler + { + private const float CRTPenaltyPerLevel = 1f; + + /// + /// Starts buff, reducing Crit rate + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var reduceCrt = buff.Target.Properties.GetFloat(PropertyName.CRTHR) * buff.NumArg1 * CRTPenaltyPerLevel; + + AddPropertyModifier(buff, buff.Target, PropertyName.CRTHR_BM, -reduceCrt); + } + + /// + /// Ends the buff, resetting Crit rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.CRTHR_BM); + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs new file mode 100644 index 000000000..39af8e442 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/BullyPainBarrier_Buff.cs @@ -0,0 +1,77 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Scripting.AI; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Melia.Zone.World.Actors.Monsters; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Buff handler for Bully Pain Barrier Buff, which increases evasion + /// and adds threat on successful evade + /// It's completely identical to Bully Buff except that it also + /// grants immunity to knockback and Outlaw12's effect is nerfed + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.BullyPainBarrier_Buff)] + public class BullyPainBarrier_Buff : BuffHandler, IBuffCombatDefenseAfterCalcHandler + { + private const float DrBuffRateBase = 0.24f; + private const float DrBuffRatePerLevel = 0.04f; + private const float HatePerLevel = 3f; + + /// + /// Starts buff, increasing dodge rate. + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var dr = buff.Target.Properties.GetFloat(PropertyName.DR); + var skillLevel = buff.NumArg1; + var rate = DrBuffRateBase + DrBuffRatePerLevel * skillLevel; + var bonus = dr * rate; + + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, bonus); + } + + /// + /// Ends the buff, resetting dodge rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + } + + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseAfterCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + if (skillHitResult.Result == HitResultType.Dodge && attacker.Components.TryGet(out var component)) + { + component.Script.QueueEventAlert(new HateIncreaseAlert(target, buff.NumArg1 * HatePerLevel)); + + // Outlaw12 adds additional duration to the buff on successful evade + // For Pain Barrier buff, the maximum increase is 2 seconds + if (target.TryGetActiveAbilityLevel(AbilityId.Outlaw12, out var level)) + { + buff.IncreaseDuration(TimeSpan.FromSeconds(Math.Max(level, 2))); + } + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs new file mode 100644 index 000000000..21e1dd388 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Bully_Buff.cs @@ -0,0 +1,76 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Scripting.AI; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; +using Melia.Zone.World.Actors.Monsters; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Buff handler for Bully Buff, which increases evasion + /// and adds threat on successful evade + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.Bully_Buff)] + public class Bully_Buff : BuffHandler, IBuffCombatDefenseAfterCalcHandler + { + private const float DrBuffRateBase = 0.24f; + private const float DrBuffRatePerLevel = 0.04f; + private const float HatePerLevel = 3f; + + /// + /// Starts buff, increasing dodge rate. + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var dr = buff.Target.Properties.GetFloat(PropertyName.DR); + var skillLevel = buff.NumArg1; + var rate = DrBuffRateBase + DrBuffRatePerLevel * skillLevel; + var bonus = dr * rate; + + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, bonus); + } + + /// + /// Ends the buff, resetting dodge rate. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + } + + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseAfterCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + if (skillHitResult.Result == HitResultType.Dodge && attacker.Components.TryGet(out var component)) + { + component.Script.QueueEventAlert(new HateIncreaseAlert(target, buff.NumArg1 * HatePerLevel)); + + // Outlaw12 adds additional duration to the buff on successful evade + // Note that it only applies to monster attacks, which is why + // it's done after the AI script check + if (target.TryGetActiveAbilityLevel(AbilityId.Outlaw12, out var level)) + { + buff.IncreaseDuration(TimeSpan.FromSeconds(level)); + } + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs new file mode 100644 index 000000000..a65d9258c --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/FireBlindly_Buff.cs @@ -0,0 +1,29 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Fire Blindly Buff, which grants forced evade + /// + [BuffHandler(BuffId.FireBlindly_Buff)] + public class FireBlindly_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler + { + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.ForcedEvade = true; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/MangleAndFireBlindly_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/MangleAndFireBlindly_Debuff.cs new file mode 100644 index 000000000..c059b2f8a --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/MangleAndFireBlindly_Debuff.cs @@ -0,0 +1,37 @@ +using Melia.Shared.Data.Database; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handle for the Mangle and Fire Blindly Debuff, which + /// reduces damage dealt to the inflictor of the buff + /// + /// + /// NumArg1: Skill Level + /// NumArg2: Extra Crit Chance + /// + [BuffHandler(BuffId.MangleAndFireBlindly_Debuff)] + public class MangleAndFireBlindly_Debuff : BuffHandler, IBuffCombatAttackBeforeCalcHandler + { + /// + /// Reduces damage dealt if the target is the caster of the buff + /// + /// + /// + /// + /// + /// + public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + if (target == buff.Caster) + { + modifier.FinalDamageMultiplier -= 0.07f * buff.OverbuffCounter; + } + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Mangle_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Mangle_Buff.cs new file mode 100644 index 000000000..39399b03d --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Mangle_Buff.cs @@ -0,0 +1,29 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for the Mangle Buff, which grants forced evade + /// + [BuffHandler(BuffId.Mangle_Buff)] + public class Mangle_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler + { + /// + /// Applies the buff's effect during the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.ForcedEvade = true; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs new file mode 100644 index 000000000..2ca0bc548 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_After_Buff.cs @@ -0,0 +1,51 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handle for the Rampage After Buff, which increases damage + /// dealt and taken by 50% and prevents the caster from evading + /// + /// + /// NumArg1: Level + /// NumArg2: None + /// + [BuffHandler(BuffId.Rampage_After_Buff)] + public class Rampage_After_Buff : BuffHandler, IBuffCombatAttackBeforeCalcHandler, IBuffCombatDefenseBeforeCalcHandler + { + private const float DamageBonus = 0.5f; + private const float DamagePenalty = 0.5f; + + /// + /// Adds the damage bonus + /// + /// + /// + /// + /// + /// + public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.FinalDamageMultiplier += DamageBonus; + } + + /// + /// Adds the damage penalty and prevents evasion + /// + /// + /// + /// + /// + /// + /// + public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.FinalDamageMultiplier += DamagePenalty; + modifier.ForcedHit = true; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs new file mode 100644 index 000000000..65fec8563 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/Rampage_Outlaw18_Buff.cs @@ -0,0 +1,36 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Skills; +using Melia.Zone.Skills.Combat; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handle for the Rampage Outlaw18 Buff, which gives 10% + /// bonus crit chance per debuff that was active when it + /// was started (which was calculated when it was applied) + /// + /// + /// NumArg1: Debuff Count + /// NumArg2: None + /// + [BuffHandler(BuffId.Rampage_Outlaw18_Buff)] + public class Rampage_Outlaw18_Buff : BuffHandler, IBuffCombatAttackBeforeCalcHandler + { + public const float CritBonusPerDebuff = 10f; + + /// + /// Adds the bonus crit chance + /// + /// + /// + /// + /// + /// + public void OnAttackBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult) + { + modifier.BonusCritChance = CritBonusPerDebuff * buff.NumArg1; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/SprinkleSands_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/SprinkleSands_Debuff.cs new file mode 100644 index 000000000..0c2a32b79 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/OutLaw/SprinkleSands_Debuff.cs @@ -0,0 +1,42 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; + +namespace Melia.Zone.Buffs.Handlers.Scouts.OutLaw +{ + /// + /// Handler for Sprinkle Sands Debuff, which reduces Accuracy and Evasion + /// + /// + /// NumArg1: Skill Level + /// NumArg2: None + /// + [BuffHandler(BuffId.SprinkleSands_Debuff)] + public class SprinkleSands_Debuff : BuffHandler + { + private const float HRPenaltyRate = 0.2f; + private const float DRPenaltyRate = 0.2f; + + /// + /// Starts buff, reducing HR and DR + /// + /// + public override void OnActivate(Buff buff, ActivationType activationType) + { + var reduceHR = buff.Target.Properties.GetFloat(PropertyName.HR) * HRPenaltyRate; + var reduceDR = buff.Target.Properties.GetFloat(PropertyName.DR) * DRPenaltyRate; + + AddPropertyModifier(buff, buff.Target, PropertyName.HR_BM, -reduceHR); + AddPropertyModifier(buff, buff.Target, PropertyName.DR_BM, -reduceDR); + } + + /// + /// Ends the buff, resetting HR and DR. + /// + /// + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.HR_BM); + RemovePropertyModifier(buff, buff.Target, PropertyName.DR_BM); + } + } +}