From c722bc0de72fb7461b6dedabbd4e63b8f2511c0c Mon Sep 17 00:00:00 2001 From: Felipe Castagnaro de Carvalho Date: Sun, 18 Aug 2024 07:21:30 -0300 Subject: [PATCH 1/4] Adding the firsts BulletMaker's skills --- src/Shared/Network/NormalOp.cs | 1 + src/ZoneServer/Buffs/Buff.cs | 23 ++- .../Scouts/BulletMaker/DoubleGun_Attack.cs | 35 ++++ .../Scouts/BulletMaker/Tase_Debuff.cs | 22 +++ src/ZoneServer/Network/Send.Normal.cs | 31 +++ .../Skills/Handlers/Common/TargetSkill.cs | 31 ++- .../Scouts/BulletMaker/BloodyOverdrive.cs | 179 ++++++++++++++++++ .../Scouts/BulletMaker/DoubleGunStance.cs | 59 ++++++ .../Scouts/BulletMaker/NapalmBullet.cs | 119 ++++++++++++ .../Handlers/Scouts/BulletMaker/Outrage.cs | 57 ++++++ .../Scouts/BulletMaker/RestInPeace.cs | 100 ++++++++++ .../Components/BuffComponent.cs | 31 ++- src/ZoneServer/World/Actors/Entity.cs | 5 +- system/scripts/zone/core/calc_character.cs | 7 +- 14 files changed, 687 insertions(+), 13 deletions(-) create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/Tase_Debuff.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/DoubleGunStance.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/NapalmBullet.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Outrage.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/RestInPeace.cs diff --git a/src/Shared/Network/NormalOp.cs b/src/Shared/Network/NormalOp.cs index 9f3d67af2..83e9c83fd 100644 --- a/src/Shared/Network/NormalOp.cs +++ b/src/Shared/Network/NormalOp.cs @@ -53,6 +53,7 @@ public static class Zone public const int Cutscene = 0x6B; public const int SetSkillSpeed = 0x77; public const int SetHitDelay = 0x78; + public const int UpdateNormalAttackSkill = 0x87; public const int SpinObject = 0x8A; public const int Unknown_A1 = 0xA1; public const int LeapJump = 0xC2; diff --git a/src/ZoneServer/Buffs/Buff.cs b/src/ZoneServer/Buffs/Buff.cs index 59d9fa5d4..c74233d34 100644 --- a/src/ZoneServer/Buffs/Buff.cs +++ b/src/ZoneServer/Buffs/Buff.cs @@ -3,6 +3,7 @@ using Melia.Shared.Game.Const; using Melia.Zone.Buffs.Base; using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.CombatEntities.Components; using Yggdrasil.Scheduling; using Yggdrasil.Util; @@ -168,7 +169,8 @@ public int OverbuffCounter /// /// /// Id of the skill associated with this buff. - public Buff(BuffId buffId, float numArg1, float numArg2, TimeSpan duration, TimeSpan runTime, ICombatEntity target, ICombatEntity caster, SkillId skillId) + /// OverBuff count, the quantity of stacking buffs + public Buff(BuffId buffId, float numArg1, float numArg2, TimeSpan duration, TimeSpan runTime, ICombatEntity target, ICombatEntity caster, SkillId skillId, int overBuffCount = 1) { this.Id = buffId; this.NumArg1 = numArg1; @@ -209,6 +211,8 @@ public Buff(BuffId buffId, float numArg1, float numArg2, TimeSpan duration, Time if (this.HasUpdateTime) this.NextUpdateTime = DateTime.Now.Add(this.UpdateTime); + + this.OverbuffCounter = overBuffCount; } /// @@ -220,6 +224,15 @@ public void IncreaseOverbuff() this.OverbuffCounter++; } + /// + /// Update overbuff counter for a given value, capped to the buff's max overbuff + /// value. + /// + public void UpdateOverbuff(int value) + { + this.OverbuffCounter += value; + } + /// /// Extends the buff's duration and executes the buff handler's start /// behavior. Does not add the buff to the actor. @@ -251,6 +264,14 @@ internal void End() this.Handler?.OnEnd(this); } + /// + /// Removes/Ends the Buff + /// + internal void Stop() + { + this.Target.Components.Get()?.Stop(this.Id); + } + /// /// Updates the buff and handles effects that happen while the buff /// is active. diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs new file mode 100644 index 000000000..54b4e095a --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs @@ -0,0 +1,35 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Network; +using Melia.Zone.World.Actors.Characters; + +namespace Melia.Zone.Buffs.Handlers.Scouts.BulletMaker +{ + /// + /// Handle for the Double Gun Stance Buff, enables movement while attacking + /// + [BuffHandler(BuffId.DoubleGunStance_Buff)] + public class DoubleGunStance_Buff : BuffHandler + { + public override void OnStart(Buff buff) + { + AddPropertyModifier(buff, buff.Target, PropertyName.MovingShot_BM, this.GetMovingShotBonus(buff)); + + if (buff.Target is Character character) + Send.ZC_MOVE_SPEED(character); + } + + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.MovingShot_BM); + + if (buff.Target is Character character) + Send.ZC_MOVE_SPEED(character); + } + + private float GetMovingShotBonus(Buff buff) + { + return 1f; + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/Tase_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/Tase_Debuff.cs new file mode 100644 index 000000000..e83fed3b4 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/Tase_Debuff.cs @@ -0,0 +1,22 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; + +namespace Melia.Zone.Buffs.Handlers.Scouts.BulletMaker +{ + /// + /// Handle for the Tase Debuff, which makes the target receive additional Lightning Property damage when hit + /// + [BuffHandler(BuffId.Tase_Debuff)] + public class Tase_Debuff : BuffHandler + { + public override void OnStart(Buff buff) + { + // @TODO: Reduce the Lightning property resistance of the target + } + + public override void OnEnd(Buff buff) + { + // @TODO: Increase the Lightning property resistance of the target + } + } +} diff --git a/src/ZoneServer/Network/Send.Normal.cs b/src/ZoneServer/Network/Send.Normal.cs index 753834aa2..32bdb8d41 100644 --- a/src/ZoneServer/Network/Send.Normal.cs +++ b/src/ZoneServer/Network/Send.Normal.cs @@ -1320,6 +1320,37 @@ public static void UpdateCollection(Character character, int collectionId, int i character.Connection.Send(packet); } + + /// + /// Updates the character normal attack stance attack + /// + /// + /// + public static void UpdateNormalAttackSkill(ICombatEntity entity, SkillId skillId) + { + var packet = new Packet(Op.ZC_NORMAL); + packet.PutInt(NormalOp.Zone.UpdateNormalAttackSkill); + + packet.PutInt(entity.Handle); + packet.PutInt((int)skillId); + + entity.Map.Broadcast(packet, entity); + } + + /// + /// Purpose unknown. Seems to enable smooth movement while normal attacking. + /// + /// + public static void Skill_45(ICombatEntity entity) + { + var packet = new Packet(Op.ZC_NORMAL); + packet.PutInt(NormalOp.Zone.Skill_45); + + packet.PutInt(entity.Handle); + packet.PutByte(0); + + entity.Map.Broadcast(packet, entity); + } } } } diff --git a/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs b/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs index 804a8de6e..ac1478da5 100644 --- a/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs +++ b/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs @@ -1,6 +1,7 @@ using System; 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; @@ -13,7 +14,7 @@ namespace Melia.Zone.Skills.Handlers.Common /// /// Handles ranged skills that target a single entity. /// - [SkillHandler(SkillId.Bow_Attack, SkillId.Magic_Attack, SkillId.Pistol_Attack)] + [SkillHandler(SkillId.Bow_Attack, SkillId.Magic_Attack, SkillId.Pistol_Attack, SkillId.DoubleGun_Attack)] public class TargetSkill : ITargetSkillHandler { private const int DoubleAttackRate = 40; @@ -32,13 +33,12 @@ public void Handle(Skill skill, ICombatEntity caster, ICombatEntity target) return; } + Send.ZC_NORMAL.Skill_45(caster); + skill.IncreaseOverheat(); caster.TurnTowards(target); caster.SetAttackState(true); - //Send.ZC_SKILL_READY(caster, skill, target.Position, Position.Zero); - //Send.ZC_NORMAL.Unkown_1c(caster, target.Handle, target.Position, caster.Position.GetDirection(target.Position), Position.Zero); - if (target == null) { Send.ZC_SKILL_FORCE_TARGET(caster, null, skill, null); @@ -50,11 +50,28 @@ public void Handle(Skill skill, ICombatEntity caster, ICombatEntity target) var modifier = SkillModifier.Default; - // Random chance to trigger double hit with pistol while buff is active - if (skill.Id == SkillId.Pistol_Attack && caster.IsBuffActive(BuffId.DoubleAttack_Buff)) + Send.ZC_SKILL_READY(caster, skill, caster.Position, target.Position); + Send.ZC_NORMAL.UpdateSkillEffect(caster, 0, caster.Position, caster.Direction, Position.Zero); + + if (skill.Id == SkillId.Pistol_Attack) + { + // Random chance to trigger double hit with pistol while buff is active + if (caster.IsBuffActive(BuffId.DoubleAttack_Buff) && RandomProvider.Get().Next(100) < DoubleAttackRate) + { + modifier.HitCount = 2; + } + } + + if (skill.Id == SkillId.DoubleGun_Attack) { - if (RandomProvider.Get().Next(100) < DoubleAttackRate) + if (caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) + { + // Increase by one the stack count for Overheating buff + if (!caster.IsBuffActive(BuffId.Outrage_Buff)) + caster.StartBuff(BuffId.Overheating_Buff, TimeSpan.FromSeconds(35)); + modifier.HitCount = 2; + } } var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs new file mode 100644 index 000000000..63dd6f01b --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Yggdrasil.Util; +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 static Melia.Shared.Util.TaskHelper; +using static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker +{ + /// + /// Handles the Bullet Maker's skill Bloody Overdrive. + /// + [SkillHandler(SkillId.Bulletmarker_BloodyOverdrive)] + public class BloodyOverdrive : IGroundSkillHandler + { + // Since it's not know the internals for this skill + // We are randomizing it a bit to feel more smooth + private const int HitAttackChance = 50; + + /// + /// Handles the skill, shots the pistol around damaging nearby enemies + /// + /// + /// + /// + /// + 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; + } + + if (!caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) + return; + + skill.IncreaseOverheat(); + caster.TurnTowards(target); + caster.SetAttackState(true); + + Send.ZC_SKILL_READY(caster, skill, caster.Position, caster.Position); + Send.ZC_NORMAL.UpdateSkillEffect(caster, caster.Handle, caster.Position, caster.Direction, Position.Zero); + + // Increase by one the stack count for Overheating buff + if (!caster.IsBuffActive(BuffId.Outrage_Buff)) + caster.StartBuff(BuffId.Overheating_Buff, TimeSpan.FromSeconds(35)); + + // @TODO: Can't be knockdown back/down while casting the skill + caster.StartBuff(BuffId.Skill_SuperArmor_Buff, TimeSpan.FromSeconds(1)); + + // Bloody Overdrive: Invincible + if (caster.IsAbilityActive(AbilityId.Bulletmarker12)) + { + caster.StartBuff(BuffId.Skill_NoDamage_Buff, TimeSpan.FromSeconds(1)); + } + + if (caster.TryGetBuff(BuffId.Outrage_Buff, out var outrageBuff) && outrageBuff.OverbuffCounter > 0) + { + caster.Components.Get().Overbuff(outrageBuff, -1); + } + + CallSafe(this.Attack(skill, caster)); + } + + /// + /// Execute the attack to nearby enemies wihtin a delay + /// + /// + /// + /// + private async Task Attack(Skill skill, ICombatEntity caster) + { + var splashArea = new Circle(caster.Position, 80); + + Debug.ShowShape(caster.Map, splashArea, TimeSpan.FromSeconds(2)); + + var tagets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + var indexHelper = 0; + var rnd = RandomProvider.Get(); + + Send.ZC_SKILL_MELEE_GROUND(caster, skill, caster.Position, null); + + for (int i = 0; i < 4; i++) + { + if (rnd.Next(100) < HitAttackChance) + continue; + + var skillHits = new List(); + + foreach (var hitTarget in tagets.LimitRandom(23)) + { + this.AddSkillHitInfo(caster, hitTarget, skill, TimeSpan.Zero, skillHits, indexHelper); + } + + await Task.Delay(TimeSpan.FromMilliseconds(400)); + + Send.ZC_SKILL_HIT_INFO(caster, skillHits); + } + + // Bloody Overdrive: Ricochet + if (caster.IsAbilityActive(AbilityId.Bulletmarker8)) + { + foreach (var hitTarget in tagets.LimitBySDR(caster, skill)) + { + if (this.TryGetRicochetTarget(caster, hitTarget, skill, out var ricochetTarget)) + { + var skillHitResult = SCR_SkillHit(caster, ricochetTarget, skill); + ricochetTarget.TakeDamage(skillHitResult.Damage, caster); + + var hit = new HitInfo(caster, hitTarget, skill, skillHitResult); + hit.ForceId = ForceId.GetNew(); + hit.ResultType = HitResultType.Hit; + + Send.ZC_HIT_INFO(caster, ricochetTarget, hit); + } + } + } + + } + + /// + /// Adds a new HitInfo to the processing list + /// + /// + /// + /// + /// + /// + /// + private void AddSkillHitInfo(ICombatEntity caster, ICombatEntity target, Skill skill, TimeSpan skillHitDelay, List skillHits, int indexHelper) + { + indexHelper++; + + var skillHitResult = SCR_SkillHit(caster, target, skill); + + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, TimeSpan.FromMilliseconds(400 * indexHelper), skillHitDelay); + + skillHits.Add(skillHit); + } + + /// + /// Returns the closest target to the main target to ricochet the attack off to. + /// + /// + /// + /// + /// + /// + private bool TryGetRicochetTarget(ICombatEntity caster, ICombatEntity mainTarget, Skill skill, out ICombatEntity ricochetTarget) + { + var splashPos = caster.Position; + var splashRadius = 50; + var splashArea = new Circle(mainTarget.Position, splashRadius); + + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + if (!targets.Any()) + { + ricochetTarget = null; + return false; + } + + ricochetTarget = targets.GetClosest(mainTarget.Position, a => a != mainTarget); + return ricochetTarget != null; + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/DoubleGunStance.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/DoubleGunStance.cs new file mode 100644 index 000000000..cad38cf6f --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/DoubleGunStance.cs @@ -0,0 +1,59 @@ +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; +using Melia.Zone.World.Actors.Characters; + +namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker +{ + /// + /// Handles the Bullet Maker's skill Double Gun Stance. + /// + [SkillHandler(SkillId.Bulletmarker_DoubleGunStance)] + public class DoubleGunStance : ISelfSkillHandler + { + /// + /// Handles the skill start the Double Gun Stance buff + /// + /// + /// + /// + /// + 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); + + if (caster is Character casterCharacter) + { + var rightHand = casterCharacter.Inventory.GetItem(EquipSlot.RightHand); + if (rightHand == null || rightHand.Data.EquipType1 != EquipType.Pistol) + return; + } + + if (caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) + { + Send.ZC_NORMAL.UpdateNormalAttackSkill(caster, SkillId.Pistol_Attack); + caster.StopBuff(BuffId.DoubleGunStance_Buff); + } + else + { + Send.ZC_NORMAL.UpdateNormalAttackSkill(caster, SkillId.DoubleGun_Attack); + caster.StartBuff(BuffId.DoubleGunStance_Buff, 1, 0, TimeSpan.Zero, caster); + } + + Send.ZC_SKILL_READY(caster, skill, originPos, originPos); + Send.ZC_NORMAL.UpdateSkillEffect(caster, caster.Handle, originPos, caster.Direction, Position.Zero); + Send.ZC_SKILL_MELEE_TARGET(caster, skill, caster, null); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/NapalmBullet.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/NapalmBullet.cs new file mode 100644 index 000000000..fd3dc564d --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/NapalmBullet.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +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 static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker +{ + /// + /// Handles the Bullet Maker's skill Napalm Bullet. + /// + [SkillHandler(SkillId.Bulletmarker_NapalmBullet)] + public class NapalmBullet : ITargetSkillHandler + { + /// + /// Handles the skill, shoot with the pistol at the enemy and hit others on the way. + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + if (!caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) + return; + + skill.IncreaseOverheat(); + caster.TurnTowards(target); + caster.SetAttackState(true); + + if (target == null) + { + Send.ZC_SKILL_FORCE_TARGET(caster, null, skill, null); + return; + } + + if (!caster.InSkillUseRange(skill, target)) + { + caster.ServerMessage(Localization.Get("Too far away.")); + Send.ZC_SKILL_FORCE_TARGET(caster, null, skill, null); + return; + } + + Send.ZC_SKILL_READY(caster, skill, caster.Position, target.Position); + Send.ZC_NORMAL.UpdateSkillEffect(caster, caster.Handle, caster.Position, caster.Direction, Position.Zero); + + // Increase by one the stack count for Overheating buff + if (!caster.IsBuffActive(BuffId.Outrage_Buff)) + caster.StartBuff(BuffId.Overheating_Buff, TimeSpan.FromSeconds(35)); + + var skillHits = new List(); + this.AddSkillHitInfo(caster, target, skill, skillHits); + + var splashArea = new Square(caster.Position, caster.Direction, 130, 45); + + var otherTargets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var otherTarget in otherTargets.LimitBySDR(caster, skill)) + { + if (otherTarget.Handle == target.Handle) + continue; + + this.AddSkillHitInfo(caster, otherTarget, skill, skillHits); + } + + Send.ZC_SKILL_FORCE_TARGET(caster, target, skill, skillHits); + + if (caster.TryGetBuff(BuffId.Outrage_Buff, out var outrageBuff)) + { + if (outrageBuff.OverbuffCounter > 0) + { + foreach (var otherTarget in otherTargets.LimitBySDR(caster, skill)) + { + var skillHitResult = SCR_SkillHit(caster, otherTarget, skill); + otherTarget.TakeDamage(skillHitResult.Damage, caster); + var hit = new HitInfo(caster, otherTarget, skill, skillHitResult.Damage, skillHitResult.Result); + Send.ZC_HIT_INFO(caster, otherTarget, hit); + otherTarget.StartBuff(BuffId.Tase_Debuff, TimeSpan.FromSeconds(10)); + } + + caster.Components.Get().Overbuff(outrageBuff, -1); + } + } + } + + /// + /// Adds a new HitInfo to the processing list + /// + /// + /// + /// + /// + private void AddSkillHitInfo(ICombatEntity caster, ICombatEntity target, Skill skill, List skillHits) + { + var damageDelay = TimeSpan.FromMilliseconds(200); + var skillHitDelay = TimeSpan.FromMilliseconds(300); + var modifier = SkillModifier.Default; + modifier.HitCount = 2; + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + skillHits.Add(skillHit); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Outrage.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Outrage.cs new file mode 100644 index 000000000..2706de2d9 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Outrage.cs @@ -0,0 +1,57 @@ +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.BulletMaker +{ + /// + /// Handler for the Bullet Maker's skill Outrage. + /// + [SkillHandler(SkillId.Bulletmarker_Outrage)] + public class Outrage : IGroundSkillHandler + { + /// + /// Handles skill, applies buff. + /// + /// + /// + /// + /// + 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); + + // Cast re-cast if we already have Outrage Buff + if (caster.IsBuffActive(BuffId.Outrage_Buff)) + return; + + if (!caster.TryGetBuff(BuffId.Overheating_Buff, out var overheatingBuff) || overheatingBuff.OverbuffCounter < 4) + return; + + var overBuffCounter = 0; + + if (overheatingBuff.OverbuffCounter == 40) + overBuffCounter = 30; + else + overBuffCounter = (int)Math.Truncate((float)(overheatingBuff.OverbuffCounter / 2)); + + overheatingBuff.Stop(); + caster.StartBuff(BuffId.Outrage_Buff, TimeSpan.Zero, overBuffCounter); + + Send.ZC_SKILL_READY(caster, skill, originPos, farPos); + Send.ZC_NORMAL.UpdateSkillEffect(caster, caster.Handle, originPos, caster.Direction, Position.Zero); + Send.ZC_SKILL_MELEE_GROUND(caster, skill, farPos, null); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/RestInPeace.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/RestInPeace.cs new file mode 100644 index 000000000..b4fcf12f8 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/RestInPeace.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +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 static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker +{ + /// + /// Handles the Bullet Maker's skill Rest In Peace (R.I.P.). + /// + [SkillHandler(SkillId.Bulletmarker_RestInPeace)] + public class RestInPeace : IGroundSkillHandler + { + /// + /// Handles the skill, shoot with the pistol and hits enemies in front + /// + /// + /// + /// + /// + 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; + } + + if (!caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) + return; + + skill.IncreaseOverheat(); + caster.TurnTowards(target); + caster.SetAttackState(true); + + Send.ZC_SKILL_READY(caster, skill, caster.Position, caster.Position); + Send.ZC_NORMAL.UpdateSkillEffect(caster, caster.Handle, caster.Position, caster.Direction, Position.Zero); + + // Increase by one the stack count for Overheating buff + if (!caster.IsBuffActive(BuffId.Outrage_Buff)) + caster.StartBuff(BuffId.Overheating_Buff, TimeSpan.FromSeconds(35)); + + var skillHits1 = new List(); + var skillHits2 = new List(); + + var splashArea = new Square(caster.Position, caster.Direction, 150, 30); + + var tagets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + + foreach (var otherTarget in tagets.LimitBySDR(caster, skill)) + { + this.AddSkillHitInfo(caster, otherTarget, skill, TimeSpan.FromMilliseconds(23), TimeSpan.Zero, skillHits1); + this.AddSkillHitInfo(caster, otherTarget, skill, TimeSpan.FromMilliseconds(478), TimeSpan.Zero, skillHits2); + } + + Send.ZC_SKILL_MELEE_GROUND(caster, skill, caster.Position, skillHits1); + Send.ZC_SKILL_HIT_INFO(caster, skillHits2); + + if (caster.TryGetBuff(BuffId.Outrage_Buff, out var outrageBuff) && outrageBuff.OverbuffCounter > 0) + { + caster.Components.Get().Overbuff(outrageBuff, -1); + } + } + + /// + /// Adds a new HitInfo to the processing list + /// + /// + /// + /// + /// + /// + /// + private void AddSkillHitInfo(ICombatEntity caster, ICombatEntity target, Skill skill, TimeSpan damageDelay, TimeSpan skillHitDelay, List skillHits) + { + var modifier = SkillModifier.Default; + modifier.HitCount = 2; + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + + // Increase the damage 55% if caster has Outrage Buff + if (caster.IsBuffActive(BuffId.Outrage_Buff)) + skillHitResult.Damage *= 1.55f; + + target.TakeDamage(skillHitResult.Damage, caster); + + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + + skillHits.Add(skillHit); + } + } +} diff --git a/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs b/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs index dd9652fed..5eeb573c8 100644 --- a/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs +++ b/src/ZoneServer/World/Actors/CombatEntities/Components/BuffComponent.cs @@ -83,6 +83,32 @@ private void Overbuff(Buff buff) Send.ZC_BUFF_UPDATE(this.Entity, buff); } + /// + /// Changes the buff's overbuff and updates the client. + /// + /// + /// + public void Overbuff(Buff buff, int value) + { + var overbuff = buff.OverbuffCounter; + buff.UpdateOverbuff(value); + + if (overbuff != buff.OverbuffCounter) + { + buff.Start(); + Send.ZC_BUFF_UPDATE(this.Entity, buff); + } + else if ((overbuff + value) <= 0) + { + buff.Stop(); + } + else + { + buff.ExtendDuration(); + Send.ZC_BUFF_UPDATE(this.Entity, buff); + } + } + /// /// Adds and activates given buffs. If a buff already exists, /// it gets overbuffed. @@ -319,8 +345,9 @@ public int GetOverbuffCount(BuffId buffId) /// Custom duration of the buff. /// The entity that casted the buff. /// The id of the skill associated with the buff. + /// OverBuff count, the quantity of stacking buffs /// - public Buff Start(BuffId buffId, float numArg1, float numArg2, TimeSpan duration, ICombatEntity caster, SkillId skillId) + public Buff Start(BuffId buffId, float numArg1, float numArg2, TimeSpan duration, ICombatEntity caster, SkillId skillId, int overBuffCount = 1) { // Attempt status resistance against debuffs // TODO: Ideally, this should happen from the buff handler, @@ -337,7 +364,7 @@ public Buff Start(BuffId buffId, float numArg1, float numArg2, TimeSpan duration if (!this.TryGet(buffId, out var buff)) { - buff = new Buff(buffId, numArg1, numArg2, duration, TimeSpan.Zero, this.Entity, caster ?? this.Entity, skillId); + buff = new Buff(buffId, numArg1, numArg2, duration, TimeSpan.Zero, this.Entity, caster ?? this.Entity, skillId, overBuffCount); this.Add(buff); } else diff --git a/src/ZoneServer/World/Actors/Entity.cs b/src/ZoneServer/World/Actors/Entity.cs index 794c07983..19969a0ef 100644 --- a/src/ZoneServer/World/Actors/Entity.cs +++ b/src/ZoneServer/World/Actors/Entity.cs @@ -291,9 +291,10 @@ public static Buff StartBuff(this ICombatEntity entity, BuffId buffId) /// /// /// + /// /// - public static Buff StartBuff(this ICombatEntity entity, BuffId buffId, TimeSpan duration) - => entity.Components.Get()?.Start(buffId, 0, 0, duration, entity, SkillId.None); + public static Buff StartBuff(this ICombatEntity entity, BuffId buffId, TimeSpan duration, int overBuffCount = 1) + => entity.Components.Get()?.Start(buffId, 0, 0, duration, entity, SkillId.None, overBuffCount); /// /// Starts the buff with the given id. If the buff is already active, diff --git a/system/scripts/zone/core/calc_character.cs b/system/scripts/zone/core/calc_character.cs index 3f9d66e8e..89eb6cc4e 100644 --- a/system/scripts/zone/core/calc_character.cs +++ b/system/scripts/zone/core/calc_character.cs @@ -5,7 +5,6 @@ //--------------------------------------------------------------------------- using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using Melia.Shared.Game.Const; @@ -1394,12 +1393,17 @@ public float SCR_Get_Character_CastingSpeed(Character character) [ScriptableFunction] public float SCR_Get_Character_MovingShotable(Character character) { + Log.Info("SCR_Get_Character_MovingShotable"); + if (character.JobClass == JobClass.Archer) return 1; if (character.IsBuffActive(BuffId.Cyclone_EnableMovingShot_Buff)) return 1; + if (character.IsBuffActive(BuffId.DoubleGunStance_Buff)) + return 1; + return 0; } @@ -1412,6 +1416,7 @@ public float SCR_Get_Character_MovingShotable(Character character) public float SCR_Get_Character_MovingShot(Character character) { var canMoveWhileShooting = character.Properties.GetFloat(PropertyName.MovingShotable) == 1; + if (!canMoveWhileShooting) return 0; From f0689776acc57b53d938c4ad6142ec9909bcd832 Mon Sep 17 00:00:00 2001 From: Felipe Castagnaro de Carvalho Date: Sun, 18 Aug 2024 19:58:35 -0300 Subject: [PATCH 2/4] Small update on movement (BulletMaker) --- .../Scouts/BulletMaker/DoubleGun_Attack.cs | 4 +++ .../Scouts/BulletMaker/BloodyOverdrive.cs | 26 +++++++------------ system/scripts/zone/core/calc_character.cs | 3 --- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs index 54b4e095a..d5b3a6dcb 100644 --- a/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs +++ b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs @@ -15,6 +15,8 @@ public override void OnStart(Buff buff) { AddPropertyModifier(buff, buff.Target, PropertyName.MovingShot_BM, this.GetMovingShotBonus(buff)); + buff.Target.Properties.Invalidate(PropertyName.MovingShotable); + if (buff.Target is Character character) Send.ZC_MOVE_SPEED(character); } @@ -23,6 +25,8 @@ public override void OnEnd(Buff buff) { RemovePropertyModifier(buff, buff.Target, PropertyName.MovingShot_BM); + buff.Target.Properties.Invalidate(PropertyName.MovingShotable); + if (buff.Target is Character character) Send.ZC_MOVE_SPEED(character); } diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs index 63dd6f01b..28d66e257 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs @@ -23,10 +23,6 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker [SkillHandler(SkillId.Bulletmarker_BloodyOverdrive)] public class BloodyOverdrive : IGroundSkillHandler { - // Since it's not know the internals for this skill - // We are randomizing it a bit to feel more smooth - private const int HitAttackChance = 50; - /// /// Handles the skill, shots the pistol around damaging nearby enemies /// @@ -83,8 +79,6 @@ private async Task Attack(Skill skill, ICombatEntity caster) { var splashArea = new Circle(caster.Position, 80); - Debug.ShowShape(caster.Map, splashArea, TimeSpan.FromSeconds(2)); - var tagets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); var indexHelper = 0; var rnd = RandomProvider.Get(); @@ -93,19 +87,16 @@ private async Task Attack(Skill skill, ICombatEntity caster) for (int i = 0; i < 4; i++) { - if (rnd.Next(100) < HitAttackChance) - continue; - var skillHits = new List(); - foreach (var hitTarget in tagets.LimitRandom(23)) + foreach (var hitTarget in tagets.LimitBySDR(caster, skill)) { - this.AddSkillHitInfo(caster, hitTarget, skill, TimeSpan.Zero, skillHits, indexHelper); + this.AddSkillHitInfo(caster, hitTarget, skill, skillHits, indexHelper); } - await Task.Delay(TimeSpan.FromMilliseconds(400)); - Send.ZC_SKILL_HIT_INFO(caster, skillHits); + + await Task.Delay(TimeSpan.FromMilliseconds(400)); } // Bloody Overdrive: Ricochet @@ -135,18 +126,19 @@ private async Task Attack(Skill skill, ICombatEntity caster) /// /// /// - /// /// /// - private void AddSkillHitInfo(ICombatEntity caster, ICombatEntity target, Skill skill, TimeSpan skillHitDelay, List skillHits, int indexHelper) + private void AddSkillHitInfo(ICombatEntity caster, ICombatEntity target, Skill skill, List skillHits, int indexHelper) { indexHelper++; + var modifier = SkillModifier.Default; + modifier.HitCount = 2; - var skillHitResult = SCR_SkillHit(caster, target, skill); + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); target.TakeDamage(skillHitResult.Damage, caster); - var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, TimeSpan.FromMilliseconds(400 * indexHelper), skillHitDelay); + var skillHit = new SkillHitInfo(caster, target, skill, skillHitResult, TimeSpan.FromMilliseconds(100 * indexHelper), TimeSpan.Zero); skillHits.Add(skillHit); } diff --git a/system/scripts/zone/core/calc_character.cs b/system/scripts/zone/core/calc_character.cs index 89eb6cc4e..fd5551296 100644 --- a/system/scripts/zone/core/calc_character.cs +++ b/system/scripts/zone/core/calc_character.cs @@ -14,7 +14,6 @@ using Melia.Zone.World.Actors.Characters; using Melia.Zone.World.Actors.Characters.Components; using Melia.Zone.World.Actors.CombatEntities.Components; -using Yggdrasil.Logging; using Yggdrasil.Util; public class CharacterCalculationsScript : GeneralScript @@ -1393,8 +1392,6 @@ public float SCR_Get_Character_CastingSpeed(Character character) [ScriptableFunction] public float SCR_Get_Character_MovingShotable(Character character) { - Log.Info("SCR_Get_Character_MovingShotable"); - if (character.JobClass == JobClass.Archer) return 1; From 6d56d3f23f9be08c8d694c2a2df79f1c322b0975 Mon Sep 17 00:00:00 2001 From: Felipe Castagnaro de Carvalho Date: Mon, 19 Aug 2024 01:23:53 -0300 Subject: [PATCH 3/4] Finishing adding all BulletMaker's skills --- .../Buffs/Handlers/Common/Freeze.cs | 39 ++++ .../Scouts/BulletMaker/DoubleGun_Attack.cs | 2 +- .../BulletMaker/FreezeBullet_Cold_Debuff.cs | 41 ++++ .../Ardito/Arditi_TreGranata_DamagePad.cs | 1 - .../BulletMaker/Bulletmarker_FreezeBullet.cs | 45 +++++ .../Skills/Handlers/Common/TargetSkill.cs | 55 +++++- .../Scouts/BulletMaker/BloodyOverdrive.cs | 28 ++- .../Scouts/BulletMaker/FreezeBullet.cs | 51 +++++ .../Scouts/BulletMaker/MozambiqueDrill.cs | 183 ++++++++++++++++++ .../Scouts/BulletMaker/TracerBullet.cs | 43 ++++ 10 files changed, 481 insertions(+), 7 deletions(-) create mode 100644 src/ZoneServer/Buffs/Handlers/Common/Freeze.cs create mode 100644 src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/FreezeBullet_Cold_Debuff.cs create mode 100644 src/ZoneServer/Pads/Handlers/Scout/BulletMaker/Bulletmarker_FreezeBullet.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/FreezeBullet.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/MozambiqueDrill.cs create mode 100644 src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs diff --git a/src/ZoneServer/Buffs/Handlers/Common/Freeze.cs b/src/ZoneServer/Buffs/Handlers/Common/Freeze.cs new file mode 100644 index 000000000..33c2e0b04 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Common/Freeze.cs @@ -0,0 +1,39 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Network; +using Melia.Zone.World.Actors.CombatEntities.Components; + +namespace Melia.Zone.Buffs.Handlers.Common +{ + /// + /// Handler for Freeze, which applies hold on the target + /// + [BuffHandler(BuffId.Freeze)] + public class Freeze : BuffHandler + { + /// + /// Starts buff + /// + /// + public override void OnStart(Buff buff) + { + var target = buff.Target; + + if (target.Components.TryGet(out var movementComponent)) + movementComponent.ApplyHold(); + } + + /// + /// Ends the buff + /// + /// + public override void OnEnd(Buff buff) + { + var target = buff.Target; + + if (target.Components.TryGet(out var movementComponent)) + movementComponent.ReleaseHold(); + } + } +} diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs index d5b3a6dcb..2eeb72d86 100644 --- a/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs +++ b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/DoubleGun_Attack.cs @@ -33,7 +33,7 @@ public override void OnEnd(Buff buff) private float GetMovingShotBonus(Buff buff) { - return 1f; + return 0.8f; } } } diff --git a/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/FreezeBullet_Cold_Debuff.cs b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/FreezeBullet_Cold_Debuff.cs new file mode 100644 index 000000000..88b97ae67 --- /dev/null +++ b/src/ZoneServer/Buffs/Handlers/Scouts/BulletMaker/FreezeBullet_Cold_Debuff.cs @@ -0,0 +1,41 @@ +using System; +using Melia.Shared.Game.Const; +using Melia.Zone.Buffs.Base; +using Melia.Zone.Network; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Buffs.Handlers.Scouts.BulletMaker +{ + /// + /// Handle for the FreezeBullet Cold Debuff, which freezes the enemy for 2 seconds upon reaching 4 stacks + /// + [BuffHandler(BuffId.FreezeBullet_Cold_Debuff)] + public class FreezeBullet_Cold_Debuff : BuffHandler + { + public override void OnStart(Buff buff) + { + if (!buff.Vars.GetBool("Slow_FreezeBullet_Cold_Debuff")) + { + var reduceMspd = buff.Target.Properties.GetFloat(PropertyName.MSPD) * 0.5f; + + AddPropertyModifier(buff, buff.Target, PropertyName.MSPD_BM, -reduceMspd); + Send.ZC_MSPD(buff.Target); + + buff.Vars.SetBool("Slow_FreezeBullet_Cold_Debuff", true); + } + + if (buff.OverbuffCounter >= 4) + { + buff.Target.StartBuff(BuffId.Freeze, 0f, 0f, TimeSpan.FromSeconds(2), buff.Caster); + buff.Stop(); + } + } + + public override void OnEnd(Buff buff) + { + RemovePropertyModifier(buff, buff.Target, PropertyName.MSPD_BM); + Send.ZC_MSPD(buff.Target); + buff.Vars.SetBool("Slow_FreezeBullet_Cold_Debuff", false); + } + } +} diff --git a/src/ZoneServer/Pads/Handlers/Scout/Ardito/Arditi_TreGranata_DamagePad.cs b/src/ZoneServer/Pads/Handlers/Scout/Ardito/Arditi_TreGranata_DamagePad.cs index 9f946b2b2..14dec455d 100644 --- a/src/ZoneServer/Pads/Handlers/Scout/Ardito/Arditi_TreGranata_DamagePad.cs +++ b/src/ZoneServer/Pads/Handlers/Scout/Ardito/Arditi_TreGranata_DamagePad.cs @@ -1,5 +1,4 @@ using Melia.Zone.Network; -using Melia.Zone.Skills; using Melia.Zone.World.Actors.Monsters; namespace Melia.Zone.Pads.Handlers.Scout.Ardito diff --git a/src/ZoneServer/Pads/Handlers/Scout/BulletMaker/Bulletmarker_FreezeBullet.cs b/src/ZoneServer/Pads/Handlers/Scout/BulletMaker/Bulletmarker_FreezeBullet.cs new file mode 100644 index 000000000..ae67b0959 --- /dev/null +++ b/src/ZoneServer/Pads/Handlers/Scout/BulletMaker/Bulletmarker_FreezeBullet.cs @@ -0,0 +1,45 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Network; +using Melia.Zone.World.Actors; +using Melia.Zone.World.Actors.Monsters; + +namespace Melia.Zone.Pads.Handlers.Scout.Ardito +{ + /// + /// Handler for the Bullet Marker Freeze Bullet pad, creates and disables the effect + /// + [PadHandler(PadName.Bulletmarker_FreezeBullet)] + public class Bulletmarker_FreezeBullet : ICreatePadHandler, IDestroyPadHandler + { + /// + /// Called when the pad is created. + /// + /// + /// + public void Created(object sender, PadTriggerArgs args) + { + var pad = args.Trigger; + var creator = args.Creator; + + Send.ZC_NORMAL.PadUpdate(creator, pad, PadName.Bulletmarker_FreezeBullet, 0, 72.30239f, 50, true); + } + + /// + /// Called when the pad is destroyed. + /// + /// + /// + public void Destroyed(object sender, PadTriggerArgs args) + { + var pad = args.Trigger; + var creator = args.Creator; + + if (creator.TryGetSkill(SkillId.Bulletmarker_FreezeBullet, out var freezeSkill)) + { + freezeSkill.Vars.SetBool("Pad_" + PadName.Bulletmarker_FreezeBullet, false); + } + + Send.ZC_NORMAL.PadUpdate(creator, pad, PadName.Bulletmarker_FreezeBullet, 0, 72.30239f, 50, false); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs b/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs index ac1478da5..5b73a726e 100644 --- a/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs +++ b/src/ZoneServer/Skills/Handlers/Common/TargetSkill.cs @@ -5,7 +5,10 @@ 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.Monsters; +using Melia.Zone.World.Actors.Pads; using Yggdrasil.Util; using static Melia.Zone.Skills.SkillUseFunctions; @@ -61,8 +64,7 @@ public void Handle(Skill skill, ICombatEntity caster, ICombatEntity target) modifier.HitCount = 2; } } - - if (skill.Id == SkillId.DoubleGun_Attack) + else if (skill.Id == SkillId.DoubleGun_Attack) { if (caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) { @@ -74,6 +76,36 @@ public void Handle(Skill skill, ICombatEntity caster, ICombatEntity target) } } + if (skill.Id == SkillId.DoubleGun_Attack || skill.Id == SkillId.Pistol_Attack) + { + if (caster.IsBuffActive(BuffId.FreezeBullet_Buff) && RandomProvider.Get().Next(100) < 30) + { + target.StartBuff(BuffId.Freeze, TimeSpan.FromSeconds(3)); + + //[Arts] Freeze Bullet: Fog + if (caster.IsAbilityActive(AbilityId.Bulletmarker16)) + { + // Only one Pad will be created + if (caster.TryGetSkill(SkillId.Bulletmarker_FreezeBullet, out var freezeSkill) && !freezeSkill.Vars.GetBool("Pad_" + PadName.Bulletmarker_FreezeBullet, false)) + { + freezeSkill.Vars.SetBool("Pad_" + PadName.Bulletmarker_FreezeBullet, true); + + var pad = new Pad(PadName.Bulletmarker_FreezeBullet, caster, freezeSkill, new Circle(target.Position, 45)); + + pad.Position = target.Position; + pad.Trigger.LifeTime = TimeSpan.FromSeconds(8); + pad.Trigger.UpdateInterval = TimeSpan.FromSeconds(1); + pad.Trigger.MaxActorCount = 10; + + pad.Trigger.Subscribe(TriggerType.Update, this.OnFreezePadTriggerUpdate); + + caster.Map.AddPad(pad); + } + } + } + + } + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); target.TakeDamage(skillHitResult.Damage, caster); @@ -82,5 +114,24 @@ public void Handle(Skill skill, ICombatEntity caster, ICombatEntity target) Send.ZC_SKILL_FORCE_TARGET(caster, target, skill, skillHit); } + + /// + /// Called when an actor enters the area of the freeze pad. + /// + /// + /// + private void OnFreezePadTriggerUpdate(object sender, PadTriggerArgs args) + { + var pad = args.Trigger; + var caster = args.Creator; + var skill = args.Skill; + + var targets = pad.Trigger.GetAttackableEntities(caster); + + foreach (var target in targets.LimitBySDR(caster, skill)) + { + target.StartBuff(BuffId.FreezeBullet_Cold_Debuff, TimeSpan.FromSeconds(2)); + } + } } } diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs index 28d66e257..737d16ad2 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs @@ -32,14 +32,32 @@ public class BloodyOverdrive : IGroundSkillHandler /// public void Handle(Skill skill, ICombatEntity caster, Position originPos, Position farPos, ICombatEntity target) { + if (!caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) + return; + if (!caster.TrySpendSp(skill)) { caster.ServerMessage(Localization.Get("Not enough SP.")); return; } - if (!caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) - return; + // Bloody Overdrive: Ricochet + if (caster.IsAbilityActive(AbilityId.Bulletmarker8)) + { + // Increase SP Consumption by 30% + var spendSp = skill.Properties.GetFloat(PropertyName.SpendSP) * 0.3f; + if (!caster.TrySpendSp(spendSp)) + return; + } + + // Bloody Overdrive: Invincible + if (caster.IsAbilityActive(AbilityId.Bulletmarker12)) + { + // Increase SP Consumption by 30% + var spendSp = skill.Properties.GetFloat(PropertyName.SpendSP) * 0.3f; + if (!caster.TrySpendSp(spendSp)) + return; + } skill.IncreaseOverheat(); caster.TurnTowards(target); @@ -58,6 +76,11 @@ public void Handle(Skill skill, ICombatEntity caster, Position originPos, Positi // Bloody Overdrive: Invincible if (caster.IsAbilityActive(AbilityId.Bulletmarker12)) { + // Increase SP Consumption by 30% + var spendSp = skill.Properties.GetFloat(PropertyName.SpendSP) * 0.3f; + if (caster.TrySpendSp(spendSp)) + return; + caster.StartBuff(BuffId.Skill_NoDamage_Buff, TimeSpan.FromSeconds(1)); } @@ -117,7 +140,6 @@ private async Task Attack(Skill skill, ICombatEntity caster) } } } - } /// diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/FreezeBullet.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/FreezeBullet.cs new file mode 100644 index 000000000..f74f0e491 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/FreezeBullet.cs @@ -0,0 +1,51 @@ +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.BulletMaker +{ + /// + /// Handles the Bullet Maker's skill Freeze Bullet. + /// + [SkillHandler(SkillId.Bulletmarker_FreezeBullet)] + public class FreezeBullet : ISelfSkillHandler + { + /// + /// Handles the skill, applies a buff to self + /// + /// + /// + /// + /// + 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); + + caster.StartBuff(BuffId.FreezeBullet_Buff, 1, 0, this.GetBuffDuration(skill), caster); + + Send.ZC_SKILL_READY(caster, skill, originPos, originPos); + Send.ZC_NORMAL.UpdateSkillEffect(caster, caster.Handle, originPos, caster.Direction, Position.Zero); + Send.ZC_SKILL_MELEE_TARGET(caster, skill, caster, null); + } + + /// + /// Returns the FreezeBullet Buff duration + /// + /// + private TimeSpan GetBuffDuration(Skill skill) + { + return TimeSpan.FromSeconds(15 + skill.Level); + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/MozambiqueDrill.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/MozambiqueDrill.cs new file mode 100644 index 000000000..d09341910 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/MozambiqueDrill.cs @@ -0,0 +1,183 @@ +using System; +using System.Linq; +using Melia.Shared.Data; +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 static Melia.Zone.Skills.SkillUseFunctions; + +namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker +{ + /// + /// Handles the Bullet Maker's skill Mozambique Drill. + /// + [SkillHandler(SkillId.Bulletmarker_MozambiqueDrill)] + public class MozambiqueDrill : ITargetSkillHandler + { + /// + /// Handles the skill, shoots with the pistol at the target enemy. + /// + /// + /// + /// + public void Handle(Skill skill, ICombatEntity caster, ICombatEntity target) + { + if (!caster.TrySpendSp(skill)) + { + caster.ServerMessage(Localization.Get("Not enough SP.")); + return; + } + + // Mozambique Drill: Ricochet + if (caster.IsAbilityActive(AbilityId.Bulletmarker10)) + { + // Increase SP Consumption by 30% + var spendSp = skill.Properties.GetFloat(PropertyName.SpendSP) * 0.3f; + if (!caster.TrySpendSp(spendSp)) + return; + } + + // Mozambique Drill: Ignore Defense + if (caster.IsAbilityActive(AbilityId.Bulletmarker9)) + { + // Increase SP Consumption by 30% + var spendSp = skill.Properties.GetFloat(PropertyName.SpendSP) * 0.3f; + if (!caster.TrySpendSp(spendSp)) + return; + } + + if (!caster.IsBuffActive(BuffId.DoubleGunStance_Buff)) + return; + + skill.IncreaseOverheat(); + caster.TurnTowards(target); + caster.SetAttackState(true); + + if (target == null) + { + Send.ZC_SKILL_FORCE_TARGET(caster, null, skill, null); + return; + } + + if (!caster.InSkillUseRange(skill, target)) + { + caster.ServerMessage(Localization.Get("Too far away.")); + Send.ZC_SKILL_FORCE_TARGET(caster, null, skill, null); + return; + } + + Send.ZC_SKILL_READY(caster, skill, caster.Position, target.Position); + Send.ZC_NORMAL.UpdateSkillEffect(caster, caster.Handle, caster.Position, caster.Direction, Position.Zero); + + // Increase by one the stack count for Overheating buff + if (!caster.IsBuffActive(BuffId.Outrage_Buff)) + caster.StartBuff(BuffId.Overheating_Buff, TimeSpan.FromSeconds(35)); + + var skillHit = this.GetSkillHitInfo(caster, target, skill); + Send.ZC_SKILL_FORCE_TARGET(caster, target, skill, skillHit); + + // Mozambique Drill: Ricochet + if (caster.IsAbilityActive(AbilityId.Bulletmarker10)) + { + if (this.TryGetRicochetTarget(caster, target, skill, out var ricochetTarget)) + { + var skillHitResult = SCR_SkillHit(caster, ricochetTarget, skill); + ricochetTarget.TakeDamage(skillHitResult.Damage, caster); + + var hit = new HitInfo(caster, target, skill, skillHitResult); + hit.ForceId = ForceId.GetNew(); + hit.ResultType = HitResultType.Hit; + + Send.ZC_HIT_INFO(caster, ricochetTarget, hit); + } + } + } + + /// + /// Get a new HitInfo + /// + /// + /// + /// + /// + private SkillHitInfo GetSkillHitInfo(ICombatEntity caster, ICombatEntity target, Skill skill) + { + var damageDelay = TimeSpan.FromMilliseconds(328); + var skillHitDelay = TimeSpan.FromMilliseconds(100); + var modifier = SkillModifier.Default; + modifier.HitCount = 2; + + if (caster.IsAbilityActive(AbilityId.Bulletmarker9)) + modifier.DefensePenetrationRate = this.GetIgnoreDefenseRatio(caster); + + var skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + + if (caster.TryGetBuff(BuffId.Outrage_Buff, out var outrageBuff)) + { + if (outrageBuff.OverbuffCounter > 0) + { + caster.Components.Get().Overbuff(outrageBuff, -1); + // Increase the HitCount by one + modifier.HitCount += 1; + + skillHitResult = SCR_SkillHit(caster, target, skill, modifier); + // Decreases the Damage by 22.5% + skillHitResult.Damage *= 0.775f; + target.TakeDamage(skillHitResult.Damage, caster); + + return new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + } + } + + target.TakeDamage(skillHitResult.Damage, caster); + + return new SkillHitInfo(caster, target, skill, skillHitResult, damageDelay, skillHitDelay); + } + + /// + /// Returns the closest target to the main target to ricochet the attack off to. + /// + /// + /// + /// + /// + /// + private bool TryGetRicochetTarget(ICombatEntity caster, ICombatEntity mainTarget, Skill skill, out ICombatEntity ricochetTarget) + { + var splashPos = caster.Position; + var splashRadius = 50; + var splashArea = new Circle(mainTarget.Position, splashRadius); + + var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea); + if (!targets.Any()) + { + ricochetTarget = null; + return false; + } + + ricochetTarget = targets.GetClosest(mainTarget.Position, a => a != mainTarget); + return ricochetTarget != null; + } + + /// + /// Returns the Ignore Defense Ratio once 'Mozambique Drill: Ignore Defense' is enabled + /// + /// + /// + private float GetIgnoreDefenseRatio(ICombatEntity caster) + { + if (caster.TryGetAbility(AbilityId.Bulletmarker9, out var ability)) + { + return (ability.Level * 2) / 2; + } + + return 0; + } + } +} diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs new file mode 100644 index 000000000..14dfade78 --- /dev/null +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs @@ -0,0 +1,43 @@ +using Melia.Shared.Game.Const; +using Melia.Zone.Skills.Combat; +using Melia.Zone.Skills.Handlers.Base; +using Melia.Zone.World.Actors; + +namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker +{ + /// + /// Handler for the passive Bullet Maker skill Tracer Bullet. + /// + [SkillHandler(SkillId.Bulletmarker_TracerBullet)] + public class TracerBullet : ISkillHandler, ISkillCombatAttackBeforeCalcHandler + { + /// + /// Applies the skill's effect before the combat calculations. + /// + /// + /// + /// + /// + /// + /// + public void OnAttackBeforeCalc(Skill skill, ICombatEntity attacker, ICombatEntity target, Skill attackerSkill, SkillModifier modifier, SkillHitResult skillHitResult) + { + // Increase Accuracy by a percentage + var accuracyRateBonus = this.GetAccuracyRateBonus(skill); + attacker.Properties.Modify(PropertyName.HR_RATE_BM, accuracyRateBonus); + + // Increase Minimum Critical Rate by a percentage + modifier.MinCritChance *= 1 + this.GetMinimumCriticalRateBonus(skill); + } + + private float GetAccuracyRateBonus(Skill skill) + { + return (10 + (skill.Level * 2)) / 100; + } + + private float GetMinimumCriticalRateBonus(Skill skill) + { + return (10 + (skill.Level * 2)) / 100; + } + } +} From e3453d8a0faacdecfd0380728aed08df527ecdf3 Mon Sep 17 00:00:00 2001 From: Felipe Castagnaro Date: Thu, 22 Aug 2024 13:44:18 -0300 Subject: [PATCH 4/4] Updating the skill Class's names --- ...yOverdrive.cs => Bulletmarker_BloodyOverdrive.cs} | 2 +- ...eGunStance.cs => Bulletmarker_DoubleGunStance.cs} | 2 +- ...{FreezeBullet.cs => Bulletmarker_FreezeBullet.cs} | 2 +- ...biqueDrill.cs => Bulletmarker_MozambiqueDrill.cs} | 2 +- ...{NapalmBullet.cs => Bulletmarker_NapalmBullet.cs} | 2 +- .../{Outrage.cs => Bulletmarker_Outrage.cs} | 2 +- .../{RestInPeace.cs => Bulletmarker_RestInPeace.cs} | 2 +- .../Handlers/Scouts/BulletMaker/TracerBullet.cs | 12 +++++++++++- 8 files changed, 18 insertions(+), 8 deletions(-) rename src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/{BloodyOverdrive.cs => Bulletmarker_BloodyOverdrive.cs} (98%) rename src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/{DoubleGunStance.cs => Bulletmarker_DoubleGunStance.cs} (96%) rename src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/{FreezeBullet.cs => Bulletmarker_FreezeBullet.cs} (95%) rename src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/{MozambiqueDrill.cs => Bulletmarker_MozambiqueDrill.cs} (98%) rename src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/{NapalmBullet.cs => Bulletmarker_NapalmBullet.cs} (98%) rename src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/{Outrage.cs => Bulletmarker_Outrage.cs} (96%) rename src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/{RestInPeace.cs => Bulletmarker_RestInPeace.cs} (98%) diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_BloodyOverdrive.cs similarity index 98% rename from src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs rename to src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_BloodyOverdrive.cs index 737d16ad2..86eec7026 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/BloodyOverdrive.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_BloodyOverdrive.cs @@ -21,7 +21,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handles the Bullet Maker's skill Bloody Overdrive. /// [SkillHandler(SkillId.Bulletmarker_BloodyOverdrive)] - public class BloodyOverdrive : IGroundSkillHandler + public class Bulletmarker_BloodyOverdrive : IGroundSkillHandler { /// /// Handles the skill, shots the pistol around damaging nearby enemies diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/DoubleGunStance.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_DoubleGunStance.cs similarity index 96% rename from src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/DoubleGunStance.cs rename to src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_DoubleGunStance.cs index cad38cf6f..cbf6c1251 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/DoubleGunStance.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_DoubleGunStance.cs @@ -13,7 +13,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handles the Bullet Maker's skill Double Gun Stance. /// [SkillHandler(SkillId.Bulletmarker_DoubleGunStance)] - public class DoubleGunStance : ISelfSkillHandler + public class Bulletmarker_DoubleGunStance : ISelfSkillHandler { /// /// Handles the skill start the Double Gun Stance buff diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/FreezeBullet.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_FreezeBullet.cs similarity index 95% rename from src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/FreezeBullet.cs rename to src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_FreezeBullet.cs index f74f0e491..3ab8e3135 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/FreezeBullet.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_FreezeBullet.cs @@ -12,7 +12,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handles the Bullet Maker's skill Freeze Bullet. /// [SkillHandler(SkillId.Bulletmarker_FreezeBullet)] - public class FreezeBullet : ISelfSkillHandler + public class Bulletmarker_FreezeBullet : ISelfSkillHandler { /// /// Handles the skill, applies a buff to self diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/MozambiqueDrill.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_MozambiqueDrill.cs similarity index 98% rename from src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/MozambiqueDrill.cs rename to src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_MozambiqueDrill.cs index d09341910..33b458c3d 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/MozambiqueDrill.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_MozambiqueDrill.cs @@ -18,7 +18,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handles the Bullet Maker's skill Mozambique Drill. /// [SkillHandler(SkillId.Bulletmarker_MozambiqueDrill)] - public class MozambiqueDrill : ITargetSkillHandler + public class Bulletmarker_MozambiqueDrill : ITargetSkillHandler { /// /// Handles the skill, shoots with the pistol at the target enemy. diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/NapalmBullet.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_NapalmBullet.cs similarity index 98% rename from src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/NapalmBullet.cs rename to src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_NapalmBullet.cs index fd3dc564d..223b95c1a 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/NapalmBullet.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_NapalmBullet.cs @@ -17,7 +17,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handles the Bullet Maker's skill Napalm Bullet. /// [SkillHandler(SkillId.Bulletmarker_NapalmBullet)] - public class NapalmBullet : ITargetSkillHandler + public class Bulletmarker_NapalmBullet : ITargetSkillHandler { /// /// Handles the skill, shoot with the pistol at the enemy and hit others on the way. diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Outrage.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_Outrage.cs similarity index 96% rename from src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Outrage.cs rename to src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_Outrage.cs index 2706de2d9..96a3beb5c 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Outrage.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_Outrage.cs @@ -12,7 +12,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handler for the Bullet Maker's skill Outrage. /// [SkillHandler(SkillId.Bulletmarker_Outrage)] - public class Outrage : IGroundSkillHandler + public class Bulletmarker_Outrage : IGroundSkillHandler { /// /// Handles skill, applies buff. diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/RestInPeace.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_RestInPeace.cs similarity index 98% rename from src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/RestInPeace.cs rename to src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_RestInPeace.cs index b4fcf12f8..846c63cad 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/RestInPeace.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/Bulletmarker_RestInPeace.cs @@ -17,7 +17,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handles the Bullet Maker's skill Rest In Peace (R.I.P.). /// [SkillHandler(SkillId.Bulletmarker_RestInPeace)] - public class RestInPeace : IGroundSkillHandler + public class Bulletmarker_RestInPeace : IGroundSkillHandler { /// /// Handles the skill, shoot with the pistol and hits enemies in front diff --git a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs index 14dfade78..657a71993 100644 --- a/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs +++ b/src/ZoneServer/Skills/Handlers/Scouts/BulletMaker/TracerBullet.cs @@ -9,7 +9,7 @@ namespace Melia.Zone.Skills.Handlers.Scouts.BulletMaker /// Handler for the passive Bullet Maker skill Tracer Bullet. /// [SkillHandler(SkillId.Bulletmarker_TracerBullet)] - public class TracerBullet : ISkillHandler, ISkillCombatAttackBeforeCalcHandler + public class Bulletmarker_TracerBullet : ISkillHandler, ISkillCombatAttackBeforeCalcHandler { /// /// Applies the skill's effect before the combat calculations. @@ -30,11 +30,21 @@ public void OnAttackBeforeCalc(Skill skill, ICombatEntity attacker, ICombatEntit modifier.MinCritChance *= 1 + this.GetMinimumCriticalRateBonus(skill); } + /// + /// Returns the accuracy rate bonus applied on the skill + /// + /// + /// private float GetAccuracyRateBonus(Skill skill) { return (10 + (skill.Level * 2)) / 100; } + /// + /// Returns the minmum Critical Rate Bonus applied on the skill + /// + /// + /// private float GetMinimumCriticalRateBonus(Skill skill) { return (10 + (skill.Level * 2)) / 100;