-
Notifications
You must be signed in to change notification settings - Fork 485
Add Summoner Curse Skills and Lightning Shock Skill Fix #738
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,8 @@ namespace MUnique.OpenMU.GameLogic; | |
| /// </summary> | ||
| public static class AttackableExtensions | ||
| { | ||
| private const short ExplosionMagicEffectNumber = 75; // 0x4B | ||
|
|
||
| private static readonly IDictionary<AttributeDefinition, AttributeDefinition> ReductionModifiers = | ||
| new Dictionary<AttributeDefinition, AttributeDefinition> | ||
| { | ||
|
|
@@ -314,7 +316,8 @@ public static HitInfo GetHitInfo(this IAttackable defender, uint damage, DamageA | |
| /// <param name="target">The target.</param> | ||
| /// <param name="attacker">The attacker.</param> | ||
| /// <param name="skillEntry">The skill entry.</param> | ||
| public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, SkillEntry skillEntry) | ||
| /// <param name="hitInfo">The hit information.</param> | ||
| public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, SkillEntry skillEntry, HitInfo? hitInfo = null) | ||
| { | ||
| if (skillEntry.PowerUps is null && attacker is Player player) | ||
| { | ||
|
|
@@ -329,7 +332,7 @@ public static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAt | |
|
|
||
| var duration = target is Player ? skillEntry.PowerUpDurationPvp! : skillEntry.PowerUpDuration!; | ||
| var powerUps = target is Player ? skillEntry.PowerUpsPvp! : skillEntry.PowerUps!; | ||
| await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, duration, powerUps).ConfigureAwait(false); | ||
| await target.ApplyMagicEffectAsync(attacker, skillEntry.Skill!.MagicEffectDef!, duration, hitInfo, powerUps).ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <summary> | ||
|
|
@@ -373,8 +376,9 @@ public static async ValueTask ApplyRegenerationAsync(this IAttackable target, Pl | |
| /// <param name="target">The target.</param> | ||
| /// <param name="attacker">The attacker.</param> | ||
| /// <param name="skillEntry">The skill entry.</param> | ||
| /// <param name="hitInfo">The hit information.</param> | ||
| /// <returns>The success of the appliance.</returns> | ||
| public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, SkillEntry skillEntry) | ||
| public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, SkillEntry skillEntry, HitInfo? hitInfo = null) | ||
| { | ||
| if (!target.IsAlive) | ||
| { | ||
|
|
@@ -383,15 +387,15 @@ public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackab | |
|
|
||
| skillEntry.ThrowNotInitializedProperty(skillEntry.Skill is null, nameof(skillEntry.Skill)); | ||
| var modifier = skillEntry.Skill.ElementalModifierTarget; | ||
| if (modifier is null) | ||
| { | ||
| return false; | ||
| } | ||
| var skipModifier = skillEntry.Skill.SkipElementalModifier; | ||
|
|
||
| var resistance = target.Attributes[modifier]; | ||
| if (resistance >= 255 || !Rand.NextRandomBool(1 / (resistance + 1))) | ||
| if (modifier is not null && !skipModifier) | ||
| { | ||
| return false; | ||
| var resistance = target.Attributes[modifier]; | ||
| if (resistance >= 255 || !Rand.NextRandomBool(1 / (resistance + 1))) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| var applied = false; | ||
|
|
@@ -400,11 +404,11 @@ public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackab | |
| && !target.MagicEffectList.ActiveEffects.ContainsKey(effectDefinition.Number)) | ||
| { | ||
| // power-up is the wrong term here... it's more like a power-down ;-) | ||
| await target.ApplyMagicEffectAsync(attacker, skillEntry).ConfigureAwait(false); | ||
| await target.ApplyMagicEffectAsync(attacker, skillEntry, hitInfo).ConfigureAwait(false); | ||
| applied = true; | ||
| } | ||
|
|
||
| if (modifier == Stats.LightningResistance) | ||
| if (modifier == Stats.LightningResistance && !skipModifier) | ||
| { | ||
| await target.MoveRandomlyAsync().ConfigureAwait(false); | ||
| applied = true; | ||
|
|
@@ -422,10 +426,9 @@ public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackab | |
| /// <param name="powerUp">The power up.</param> | ||
| /// <param name="duration">The duration.</param> | ||
| /// <param name="targetAttribute">The target attribute.</param> | ||
| /// <returns> | ||
| /// The success of the appliance. | ||
| /// </returns> | ||
| public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, Skill skill, IElement? powerUp, IElement? duration, AttributeDefinition? targetAttribute) | ||
| /// <param name="hitInfo">The hit information.</param> | ||
| /// <returns>The success of the appliance.</returns> | ||
| public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackable target, IAttacker attacker, Skill skill, IElement? powerUp, IElement? duration, AttributeDefinition? targetAttribute, HitInfo? hitInfo) | ||
| { | ||
| if (!target.IsAlive) | ||
| { | ||
|
|
@@ -453,7 +456,7 @@ public static async ValueTask<bool> TryApplyElementalEffectsAsync(this IAttackab | |
| && targetAttribute is not null) | ||
| { | ||
| // power-up is the wrong term here... it's more like a power-down ;-) | ||
| await target.ApplyMagicEffectAsync(attacker, effectDefinition, duration, (targetAttribute, powerUp)).ConfigureAwait(false); | ||
| await target.ApplyMagicEffectAsync(attacker, effectDefinition, duration, hitInfo, (targetAttribute, powerUp)).ConfigureAwait(false); | ||
| applied = true; | ||
| } | ||
|
|
||
|
|
@@ -819,8 +822,9 @@ private static void GetBaseDmg(this IAttacker attacker, SkillEntry? skill, out i | |
| /// <param name="attacker">The attacker.</param> | ||
| /// <param name="magicEffectDefinition">The magic effect definition.</param> | ||
| /// <param name="duration">The duration.</param> | ||
| /// <param name="hitInfo">The hit information.</param> | ||
| /// <param name="powerUps">The power ups of the effect.</param> | ||
| private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, IElement duration, params (AttributeDefinition Target, IElement Boost)[] powerUps) | ||
| private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IAttacker attacker, MagicEffectDefinition magicEffectDefinition, IElement duration, HitInfo? hitInfo, params (AttributeDefinition Target, IElement Boost)[] powerUps) | ||
| { | ||
| float finalDuration = duration.Value; | ||
|
|
||
|
|
@@ -839,10 +843,26 @@ private static async ValueTask ApplyMagicEffectAsync(this IAttackable target, IA | |
| return; | ||
| } | ||
|
|
||
| var isPoisonEffect = magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsPoisoned); | ||
| var magicEffect = isPoisonEffect | ||
| ? new PoisonMagicEffect(powerUps[0].Boost, magicEffectDefinition, durationSpan, attacker, target) | ||
| : new MagicEffect(durationSpan, magicEffectDefinition, powerUps.Select(p => new MagicEffect.ElementWithTarget(p.Boost, p.Target)).ToArray()); | ||
| MagicEffect magicEffect; | ||
| if (magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsPoisoned)) | ||
| { | ||
| magicEffect = new PoisonMagicEffect(powerUps[0].Boost, magicEffectDefinition, durationSpan, attacker, target); | ||
| } | ||
| else if (magicEffectDefinition.PowerUpDefinitions.Any(e => e.TargetAttribute == Stats.IsBleeding)) | ||
| { | ||
| if (hitInfo is not { } hit || hit.HealthDamage + hit.ShieldDamage < 1) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var multiplier = magicEffectDefinition.Number == ExplosionMagicEffectNumber ? attacker.Attributes[Stats.BleedingDamageMultiplier] : 0.6f; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| var damage = (hit.HealthDamage + hit.ShieldDamage) * multiplier; | ||
| magicEffect = new BleedingMagicEffect(powerUps[0].Boost, magicEffectDefinition, durationSpan, attacker, target, damage); | ||
| } | ||
| else | ||
| { | ||
| magicEffect = new MagicEffect(durationSpan, magicEffectDefinition, powerUps.Select(p => new MagicEffect.ElementWithTarget(p.Boost, p.Target)).ToArray()); | ||
| } | ||
|
|
||
| await target.MagicEffectList.AddEffectAsync(magicEffect).ConfigureAwait(false); | ||
| if (target is ISupportWalk walkSupporter | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| // <copyright file="BleedingMagicEffect.cs" company="MUnique"> | ||
| // Licensed under the MIT License. See LICENSE file in the project root for full license information. | ||
| // </copyright> | ||
|
|
||
| namespace MUnique.OpenMU.GameLogic; | ||
|
|
||
| using System.Timers; | ||
| using MUnique.OpenMU.AttributeSystem; | ||
|
|
||
| /// <summary> | ||
| /// The magic effect for bleeding, which will damage the character every second until the effect ends. | ||
| /// </summary> | ||
| public sealed class BleedingMagicEffect : MagicEffect | ||
| { | ||
| private readonly Timer _damageTimer; | ||
| private readonly float _damage; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="BleedingMagicEffect"/> class. | ||
| /// </summary> | ||
| /// <param name="powerUp">The power up.</param> | ||
| /// <param name="definition">The definition.</param> | ||
| /// <param name="duration">The duration.</param> | ||
| /// <param name="attacker">The attacker.</param> | ||
| /// <param name="owner">The owner.</param> | ||
| /// <param name="damage">The bleeding damage.</param> | ||
| public BleedingMagicEffect(IElement powerUp, MagicEffectDefinition definition, TimeSpan duration, IAttacker attacker, IAttackable owner, float damage) | ||
| : base(powerUp, definition, duration) | ||
| { | ||
| this.Attacker = attacker; | ||
| this.Owner = owner; | ||
| this._damage = damage; | ||
| this._damageTimer = new Timer(1000); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. emu, zTeamS6.3 |
||
| this._damageTimer.Elapsed += this.OnDamageTimerElapsed; | ||
| this._damageTimer.Start(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the owner of the effect. | ||
| /// </summary> | ||
| public IAttackable Owner { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the attacker which applied the effect. | ||
| /// </summary> | ||
| public IAttacker Attacker { get; } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override void Dispose(bool disposing) | ||
| { | ||
| base.Dispose(disposing); | ||
| this._damageTimer.Stop(); | ||
| this._damageTimer.Dispose(); | ||
| } | ||
|
|
||
| [System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD100:Avoid async void methods", Justification = "Catching all Exceptions.")] | ||
| private async void OnDamageTimerElapsed(object? sender, ElapsedEventArgs e) | ||
| { | ||
| try | ||
| { | ||
| if (!this.Owner.IsAlive || this.IsDisposed || this.IsDisposing || this._damage <= 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| await this.Owner.ApplyBleedingDamageAsync(this.Attacker, (uint)this._damage).ConfigureAwait(false); | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| (this.Owner as ILoggerOwner)?.Logger.LogError(ex, "Error when applying bleeding damage"); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only the early seasons skills (S1, S2) use the elemental resistance checks:
emu, zTeamS6.3