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);
+ }
+ }
+}