Skip to content

Commit

Permalink
Merge pull request #471 from bernatvadell/feat/skill-drain-life
Browse files Browse the repository at this point in the history
  • Loading branch information
sven-n authored Aug 29, 2024
2 parents 0c3f833 + 7640e9f commit e1249ed
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 13 deletions.
3 changes: 2 additions & 1 deletion src/GameLogic/IAttackable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public interface IAttackable : IIdentifiable, ILocateable
/// <param name="skill">The skill.</param>
/// <param name="isCombo">If set to <c>true</c>, the attacker did a combination of skills.</param>
/// <param name="damageFactor">The damage factor.</param>
ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0);
/// <returns>Returns information about the damage inflicted.</returns>
ValueTask<HitInfo?> AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0);

/// <summary>
/// Reflects the damage which was done previously with <see cref="AttackByAsync" /> or even <see cref="ReflectDamageAsync" /> to the <paramref name="reflector" />.
Expand Down
6 changes: 4 additions & 2 deletions src/GameLogic/NPC/AttackableNpcBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@ public int Health
|| (this.SpawnArea.SpawnTrigger == SpawnTrigger.AutomaticDuringWave && (this._eventStateProvider?.IsSpawnWaveActive(this.SpawnArea.WaveNumber) ?? false));

/// <inheritdoc />
public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0)
public async ValueTask<HitInfo?> AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0)
{
if (this.Definition.ObjectKind == NpcObjectKind.Guard)
{
return;
return null;
}

var hitInfo = await attacker.CalculateDamageAsync(this, skill, isCombo, damageFactor).ConfigureAwait(false);
Expand All @@ -120,6 +120,8 @@ public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool
await playerSurrogate.Owner.AfterHitTargetAsync().ConfigureAwait(false);
}
}

return hitInfo;
}

/// <inheritdoc />
Expand Down
3 changes: 2 additions & 1 deletion src/GameLogic/NPC/SoccerBall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,11 @@ public SoccerBall(MonsterSpawnArea spawnInfo, MonsterDefinition stats, GameMap m
public DeathInformation? LastDeath => null;

/// <inheritdoc />
public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0)
public async ValueTask<HitInfo?> AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0)
{
var direction = attacker.GetDirectionTo(this);
await this.MoveToDirectionAsync(direction, skill is { }).ConfigureAwait(false);
return null;
}

/// <inheritdoc />
Expand Down
6 changes: 4 additions & 2 deletions src/GameLogic/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ public bool IsAnySelfDefenseActive()
}

/// <inheritdoc/>
public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0)
public async ValueTask<HitInfo?> AttackByAsync(IAttacker attacker, SkillEntry? skill, bool isCombo, double damageFactor = 1.0)
{
if (this.Attributes is null)
{
Expand All @@ -605,7 +605,7 @@ public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool
await observer.InvokeViewPlugInAsync<IShowHitPlugIn>(p => p.ShowHitAsync(this, hitInfo)).ConfigureAwait(false);
}

return;
return hitInfo;
}

attacker.ApplyAmmunitionConsumption(hitInfo);
Expand Down Expand Up @@ -634,6 +634,8 @@ public async ValueTask AttackByAsync(IAttacker attacker, SkillEntry? skill, bool
{
await attackerPlayer.AfterHitTargetAsync().ConfigureAwait(false);
}

return hitInfo;
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/GameLogic/PlayerActions/Skills/AreaSkillAttackAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ private async ValueTask ApplySkillAsync(Player player, SkillEntry skillEntry, IA

if (target.CheckSkillTargetRestrictions(player, skillEntry.Skill))
{
await target.AttackByAsync(player, skillEntry, isCombo).ConfigureAwait(false);
var hitInfo = await target.AttackByAsync(player, skillEntry, isCombo).ConfigureAwait(false);
await target.TryApplyElementalEffectsAsync(player, skillEntry).ConfigureAwait(false);
var baseSkill = skillEntry.GetBaseSkill();

if (player.GameContext.PlugInManager.GetStrategy<short, IAreaSkillPlugIn>(baseSkill.Number) is { } strategy)
{
await strategy.AfterTargetGotAttackedAsync(player, target, skillEntry, targetAreaCenter).ConfigureAwait(false);
await strategy.AfterTargetGotAttackedAsync(player, target, skillEntry, targetAreaCenter, hitInfo).ConfigureAwait(false);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class ChainLightningSkillPlugIn : IAreaSkillPlugIn
public short Key => 215;

/// <inheritdoc />
public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter)
public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter, HitInfo? hitInfo)
{
bool FilterTarget(IAttackable attackable)
{
Expand Down
38 changes: 38 additions & 0 deletions src/GameLogic/PlayerActions/Skills/DrainLifeSkillPlugIn.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// <copyright file="DrainLifeSkillPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.GameLogic.PlayerActions.Skills;

using System.Runtime.InteropServices;
using MUnique.OpenMU.GameLogic.Attributes;
using MUnique.OpenMU.GameLogic.PlugIns;
using MUnique.OpenMU.GameLogic.Views.Character;
using MUnique.OpenMU.Pathfinding;
using MUnique.OpenMU.PlugIns;

/// <summary>
/// Handles the drain life skill of the summoner class. Additionally to the attacked target, it regains life for damage dealt.
/// </summary>
[PlugIn(nameof(ChainLightningSkillPlugIn), "Handles the drain life skill of the summoner class. Additionally to the attacked target, it regains life for damage dealt.")]
[Guid("9A5A5671-3A8C-4C01-984F-1A8F8E0E7BDA")]
public class DrainLifeSkillPlugIn : IAreaSkillPlugIn
{
/// <inheritdoc/>
public short Key => 214;

/// <inheritdoc/>
public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter, HitInfo? hitInfo)
{
if (attacker is Player attackerPlayer && hitInfo != null && hitInfo.Value.HealthDamage > 0)
{
var playerAttributes = attackerPlayer.Attributes;

if (playerAttributes != null)
{
playerAttributes[Stats.CurrentHealth] = (uint)Math.Min(playerAttributes[Stats.MaximumHealth], playerAttributes[Stats.CurrentHealth] + hitInfo.Value.HealthDamage);
await attackerPlayer.InvokeViewPlugInAsync<IUpdateCurrentHealthPlugIn>(p => p.UpdateCurrentHealthAsync()).ConfigureAwait(false);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class EarthShakeSkillPlugIn : IAreaSkillPlugIn
public short Key => 62;

/// <inheritdoc />
public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter)
public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter, HitInfo? hitInfo)
{
if (!target.IsAlive || target is not IMovable movableTarget || target.CurrentMap is not { } currentMap)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class PlasmaStormSkillPlugIn : IAreaSkillPlugIn
public short Key => 76;

/// <inheritdoc />
public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter)
public async ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter, HitInfo? hitInfo)
{
if (target is Player targetPlayer
&& Rand.NextRandomBool(25)
Expand Down
3 changes: 2 additions & 1 deletion src/GameLogic/PlugIns/IAreaSkillPlugIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@ public interface IAreaSkillPlugIn : IStrategyPlugIn<short>
/// <param name="target">The target.</param>
/// <param name="skillEntry">The skill entry.</param>
/// <param name="targetAreaCenter">The target area center.</param>
ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter);
/// <param name="hitInfo">Hit info produced by the skill.</param>
ValueTask AfterTargetGotAttackedAsync(IAttacker attacker, IAttackable target, SkillEntry skillEntry, Point targetAreaCenter, HitInfo? hitInfo);
}
55 changes: 55 additions & 0 deletions src/Persistence/Initialization/Updates/FixDrainLifeSkillUpdate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// <copyright file="FixDrainLifeSkillUpdate.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>

namespace MUnique.OpenMU.Persistence.Initialization.Updates;

using System.Runtime.InteropServices;
using MUnique.OpenMU.DataModel.Configuration;
using MUnique.OpenMU.DataModel.Configuration.Items;
using MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.Persistence.Initialization.Skills;
using MUnique.OpenMU.PlugIns;

/// <summary>
/// This adds the items required to enter the kalima map.
/// </summary>
[PlugIn(PlugInName, PlugInDescription)]
[Guid("A8827A3C-7F52-47CF-9EA5-562A9C06B986")]
public class FixDrainLifeSkillUpdate : UpdatePlugInBase
{
/// <summary>
/// The plug in name.
/// </summary>
internal const string PlugInName = "Fix Drain Life Skill";

/// <summary>
/// The plug in description.
/// </summary>
internal const string PlugInDescription = "Updates the attributes of the summoner's Drain Life skill to make it work properly.";

/// <inheritdoc />
public override UpdateVersion Version => UpdateVersion.FixDrainLifeSkill;

/// <inheritdoc />
public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id;

/// <inheritdoc />
public override string Name => PlugInName;

/// <inheritdoc />
public override string Description => PlugInDescription;

/// <inheritdoc />
public override bool IsMandatory => false;

/// <inheritdoc />
public override DateTime CreatedAt => new(2024, 08, 29, 18, 0, 0, DateTimeKind.Utc);

/// <inheritdoc />
protected override async ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration)
{
var drainLife = gameConfiguration.Skills.First(x => x.Number == (short)SkillNumber.DrainLife);
drainLife.SkillType = SkillType.AreaSkillExplicitTarget;
}
}
5 changes: 5 additions & 0 deletions src/Persistence/Initialization/Updates/UpdateVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,9 @@ public enum UpdateVersion
/// The version of the <see cref="FixAncientDiscriminatorsUpdatePlugIn"/>.
/// </summary>
FixAncientDiscriminators = 26,

/// <summary>
/// The version of the <see cref="FixDrainLifeSkillUpdate"/>.
/// </summary>
FixDrainLifeSkill = 27,
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public override void Initialize()
this.CreateSkill(SkillNumber.SpellofRestriction, "Spell of Restriction", CharacterClasses.All, distance: 3, manaConsumption: 30, elementalModifier: ElementalType.Ice, cooldownMinutes: 5);
this.CreateSkill(SkillNumber.SpellofPursuit, "Spell of Pursuit", CharacterClasses.All, manaConsumption: 30, elementalModifier: ElementalType.Ice, cooldownMinutes: 10);
this.CreateSkill(SkillNumber.ShieldBurn, "Shield-Burn", CharacterClasses.All, distance: 3, manaConsumption: 30, elementalModifier: ElementalType.Ice, cooldownMinutes: 5);
this.CreateSkill(SkillNumber.DrainLife, "Drain Life", CharacterClasses.AllSummoners, DamageType.Curse, 35, 6, manaConsumption: 50, energyRequirement: 150);
this.CreateSkill(SkillNumber.DrainLife, "Drain Life", CharacterClasses.AllSummoners, DamageType.Curse, 35, 6, manaConsumption: 50, energyRequirement: 150, skillType: SkillType.AreaSkillExplicitTarget);
this.CreateSkill(SkillNumber.ChainLightning, "Chain Lightning", CharacterClasses.AllSummoners, DamageType.Curse, 70, 6, manaConsumption: 85, energyRequirement: 245, skillType: SkillType.AreaSkillExplicitTarget, skillTarget: SkillTarget.Explicit);
this.CreateSkill(SkillNumber.DamageReflection, "Damage Reflection", CharacterClasses.AllSummoners, distance: 5, abilityConsumption: 10, manaConsumption: 40, energyRequirement: 375);
this.CreateSkill(SkillNumber.Berserker, "Berserker", CharacterClasses.AllSummoners, DamageType.Curse, distance: 5, abilityConsumption: 50, manaConsumption: 100, energyRequirement: 620);
Expand Down

0 comments on commit e1249ed

Please sign in to comment.