Skip to content
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

Sadhu skills #302

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9d14ab5
Initial Sadhu skill's implementations
felipecastagnarodecarvalho Jul 27, 2024
0f1cfaa
[WIP] Sadhu skills
felipecastagnarodecarvalho Jul 28, 2024
8a0803a
Merge branch 'master' of https://github.com/NoCode-NoLife/melia into …
felipecastagnarodecarvalho Jul 28, 2024
cdf1740
Adding the first sadhu Skill - Prakriti
felipecastagnarodecarvalho Jul 29, 2024
01a2f3a
Polishing Prakriti
felipecastagnarodecarvalho Jul 29, 2024
bd6a41f
Adding Anila skill
felipecastagnarodecarvalho Jul 29, 2024
3c5e170
Adding Possession skill (sadhu) + skill base class inheritance
felipecastagnarodecarvalho Jul 29, 2024
80ff162
Adding Enira sadhu skill
felipecastagnarodecarvalho Jul 30, 2024
5aa0295
Working on the sadhu arts that creates clones
felipecastagnarodecarvalho Jul 30, 2024
020d514
Small Update (Sadhu)
felipecastagnarodecarvalho Jul 30, 2024
cef4651
Merge branch 'master' into sadhu_skills
felipecastagnarodecarvalho Jul 30, 2024
70535a7
[WIP] Sadhu skills tweek
felipecastagnarodecarvalho Jul 30, 2024
e2203f0
Messing up with the IA (for dummies)
felipecastagnarodecarvalho Jul 31, 2024
d3fba4d
Minor changes Sadhu
felipecastagnarodecarvalho Aug 12, 2024
60bc776
Minor update sadhu
felipecastagnarodecarvalho Aug 13, 2024
507317e
Merging master
felipecastagnarodecarvalho Aug 13, 2024
10cda3d
Updating sadhu dummy AI
felipecastagnarodecarvalho Aug 13, 2024
f2b09d5
Merging master
felipecastagnarodecarvalho Aug 15, 2024
2c6492c
Sadhu skills - Working on the hability skills
felipecastagnarodecarvalho Aug 15, 2024
9ede990
Adding last two Sadhu skills
felipecastagnarodecarvalho Aug 16, 2024
e6f87ac
Updating skill file names
felipecastagnarodecarvalho Aug 22, 2024
888b8d5
Merging master
felipecastagnarodecarvalho Sep 13, 2024
d55e4af
Merge branch 'master' into sadhu_skills
felipecastagnarodecarvalho Sep 17, 2024
73e8c55
[WIP
felipecastagnarodecarvalho Sep 17, 2024
1a49524
Resolving review conversations
felipecastagnarodecarvalho Sep 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Shared/Data/Database/SkillTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class SkillTreeDb : DatabaseJson<SkillTreeData>
/// level.
/// </summary>
/// <param name="jobId"></param>
/// <param name="circle"></param>
/// <param name="jobLevel"></param>
/// <returns></returns>
public SkillTreeData[] FindSkills(JobId jobId, int jobLevel)
{
Expand Down
1 change: 1 addition & 0 deletions src/Shared/Game/Const/HitType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ public enum HitType : short
KnockBack = 3,
KnockDown = 4,
Type18 = 18,
Type33 = 33,
}
}
3 changes: 3 additions & 0 deletions src/Shared/Network/NormalOp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static class Zone
public const int PlayEffect = 0x16;
public const int PlayForceEffect = 0x17;
public const int UpdateSkillEffect = 0x1F;
public const int UpdateModelColor = 0x20;
public const int FadeOut = 0x38;
public const int BarrackSlotCount = 0x3C;
public const int AttackCancel = 0x41;
Expand Down Expand Up @@ -67,6 +68,8 @@ public static class Zone
public const int PlayTextEffect = 0xE3;
public const int Unknown_E4 = 0xE7;
public const int Unknown_EF = 0xF2;
public const int EnableUseSkillWhileOutOfBody = 0x10B;
public const int EndOutOfBodyBuff = 0x10C;
public const int ChannelTraffic = 0x12D;
public const int SetGreetingMessage = 0x136;
public const int Unk13E = 0x13E;
Expand Down
131 changes: 131 additions & 0 deletions src/ZoneServer/Buffs/Handlers/Clerics/Sadhu/OOBE_Anila_Buff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using System;
using Melia.Shared.Game.Const;
using Melia.Zone.Buffs.Base;
using Melia.Zone.Network;
using Melia.Zone.Skills.Combat;
using Melia.Zone.Skills;
using Melia.Zone.Skills.SplashAreas;
using Melia.Zone.World.Actors;
using Melia.Zone.World.Actors.Characters;
using Melia.Zone.World.Actors.Pads;
using Melia.Zone.World.Actors.Monsters;
using static Melia.Zone.Skills.SkillUseFunctions;
using Melia.Shared.World;

namespace Melia.Zone.Buffs.Handlers.Clerics.Sadhu
{
/// <summary>
/// Handler for the Out Of Body Experience (OOBE) Anila Buff
/// which makes the character go back to original position after a while
/// and leave an effect that damages enemies on hit by a wave effect
/// </summary>
[BuffHandler(BuffId.OOBE_Anila_Buff)]
public class OOBE_Anila_Buff : Sadhu_BuffHandler_Base
{
public override void OnActivate(Buff buff, ActivationType activationType)
{
var caster = buff.Caster;

// [Arts] Spirit Expert: Wandering Soul
if (caster.IsAbilityActive(AbilityId.Sadhu35) || caster is not Character casterCharacter)
return;

var dummyCharacter = casterCharacter.Map.GetDummyCharacter((int)buff.NumArg2);

if (dummyCharacter != null)
{
dummyCharacter.Died += this.OnDummyDied;
}
}

public override void OnEnd(Buff buff)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gotta say, frankly, I'm a bit unhappy with these buff handlers. The code is pretty difficult to follow, especially with all the conditionals for dummy characters and lack of comments. Additionally, you're duplicating a lot of code even though you went to the trouble of adding a base handler class, which could handle much of it. Such as the entire OnActivate method, which is verbatim the same for all of them, the same one line to get the skill character, and more.

I would like to see these handlers receive a bit more structure and documentation, because as is, I can't follow any of this without reading, studying, and analyzing the entire code and the skill descriptions. And while this might be acceptable for the time being, at some point someone else will want to or have to change something about this job, and if they have to spend the same amount of time as you did initially writing this code, that's far from ideal.

{
var caster = buff.Caster;

if (caster is not Character casterCharacter)
return;

var skillCharacter = casterCharacter is DummyCharacter dummyCharacter && dummyCharacter.Owner.IsAbilityActive(AbilityId.Sadhu35)
? dummyCharacter.Owner
: caster;

if (skillCharacter.TryGetSkill(SkillId.Sadhu_Anila, out var skill))
{
skillCharacter.SetAttackState(true);

var pad = new Pad(PadName.Sadhu_Anila_Effect_Pad, skillCharacter, skill, new Square(caster.Position, caster.Direction, 50, 65));

pad.Position = caster.Position;
pad.Trigger.MaxActorCount = 7;
pad.Trigger.LifeTime = TimeSpan.FromSeconds(10);
pad.Trigger.Subscribe(TriggerType.Enter, this.OnCollisionEnter);

caster.Map.AddPad(pad);

// [Arts] Spirit Expert: Wandering Soul
if (casterCharacter is DummyCharacter dummyCharacter2 && dummyCharacter2.Owner.IsAbilityActive(AbilityId.Sadhu35))
{
Send.ZC_SKILL_READY(dummyCharacter2.Owner, caster, skill, caster.Position, caster.Position);
Send.ZC_NORMAL.UpdateSkillEffect(dummyCharacter2.Owner, caster.Handle, caster.Position, caster.Direction, Position.Zero);
Send.ZC_SKILL_MELEE_GROUND(dummyCharacter2.Owner, caster, skill, caster.Position, ForceId.GetNew(), null);
} else
{
skill.IncreaseOverheat();
}
}

// [Arts] Spirit Expert: Wandering Soul
if (casterCharacter is DummyCharacter dummyCharacter3 && dummyCharacter3.Owner.IsAbilityActive(AbilityId.Sadhu35))
{
this.RemoveDummyCharacter(dummyCharacter3);
return;
}

casterCharacter.Properties.Modify(PropertyName.MSPD_BM, -buff.NumArg1);

Send.ZC_NORMAL.EndOutOfBodyBuff(casterCharacter, BuffId.OOBE_Anila_Buff);
Send.ZC_NORMAL.UpdateModelColor(casterCharacter, 255, 255, 255, 255, 0.01f);

Send.ZC_PLAY_SOUND(casterCharacter, "skl_eff_yuchae_end_2");

this.ReturnToBody(casterCharacter, (int)buff.NumArg2);
}

/// <summary>
/// Called when an actor enters the skill's pad area.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void OnCollisionEnter(object sender, PadTriggerActorArgs args)
{
var pad = args.Trigger;
var creator = args.Creator;
var target = args.Initiator;

if (pad.Trigger.AtCapacity)
return;

if (!creator.CanAttack(target))
return;

this.Attack(pad.Skill, creator, target);
}

/// <summary>
/// Attacks the target
/// </summary>
/// <param name="skill"></param>
/// <param name="caster"></param>
private void Attack(Skill skill, ICombatEntity caster, ICombatEntity target)
{
var modifier = SkillModifier.MultiHit(3);
var skillHitResult = SCR_SkillHit(caster, target, skill, modifier);

target.TakeDamage(skillHitResult.Damage, caster);

var hit = new HitInfo(caster, target, skill, skillHitResult, TimeSpan.FromMilliseconds(200));

Send.ZC_HIT_INFO(caster, target, hit);
}
}
}
185 changes: 185 additions & 0 deletions src/ZoneServer/Buffs/Handlers/Clerics/Sadhu/OOBE_Moksha_Buff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using System;
using Melia.Shared.Game.Const;
using Melia.Zone.Buffs.Base;
using Melia.Zone.Network;
using Melia.Zone.Skills.Combat;
using Melia.Zone.Skills;
using Melia.Zone.Skills.SplashAreas;
using Melia.Zone.World.Actors;
using Melia.Zone.World.Actors.Characters;
using Melia.Zone.World.Actors.Pads;
using Melia.Zone.World.Actors.Monsters;
using static Melia.Zone.Skills.SkillUseFunctions;
using Melia.Shared.World;
using System.Collections.Generic;

namespace Melia.Zone.Buffs.Handlers.Clerics.Sadhu
{
/// <summary>
/// Handler for the Out Of Body Experience (OOBE) Anila Buff
/// which makes the character go back to original position after a while
/// and leave an effect that damages enemies on hit by a wave effect
/// </summary>
[BuffHandler(BuffId.OOBE_Moksha_Buff)]
public class OOBE_Moksha_Buff : Sadhu_BuffHandler_Base
{
public override void OnActivate(Buff buff, ActivationType activationType)
{
var caster = buff.Caster;

// [Arts] Spirit Expert: Wandering Soul
if (caster.IsAbilityActive(AbilityId.Sadhu35) || caster is not Character casterCharacter)
return;

var dummyCharacter = casterCharacter.Map.GetDummyCharacter((int)buff.NumArg2);

if (dummyCharacter != null)
{
dummyCharacter.Died += this.OnDummyDied;
}
}

public override void OnEnd(Buff buff)
{
var caster = buff.Caster;

if (caster is not Character casterCharacter)
return;

var skillCharacter = casterCharacter is DummyCharacter dummyCharacter && dummyCharacter.Owner.IsAbilityActive(AbilityId.Sadhu35)
? dummyCharacter.Owner
: caster;

if (skillCharacter.TryGetSkill(SkillId.Sadhu_Moksha, out var skill))
{
skillCharacter.SetAttackState(true);

var pad = new Pad(PadName.Sadhu_Moksha_Pad, skillCharacter, skill, new Circle(caster.Position, 100));

pad.Position = caster.Position;
pad.Trigger.MaxActorCount = 10;
pad.Trigger.LifeTime = TimeSpan.FromSeconds(5);
pad.Trigger.UpdateInterval = TimeSpan.FromSeconds(1);
pad.Trigger.Subscribe(TriggerType.Update, this.OnUpdate);
pad.Trigger.Subscribe(TriggerType.Destroy, this.OnDestroyPad);

caster.Map.AddPad(pad);

// [Arts] Spirit Expert: Wandering Soul
if (casterCharacter is DummyCharacter dummyCharacter2 && dummyCharacter2.Owner.IsAbilityActive(AbilityId.Sadhu35))
{
Send.ZC_SKILL_READY(dummyCharacter2.Owner, caster, skill, caster.Position, caster.Position);
Send.ZC_NORMAL.UpdateSkillEffect(dummyCharacter2.Owner, caster.Handle, caster.Position, caster.Direction, Position.Zero);
Send.ZC_SKILL_MELEE_GROUND(dummyCharacter2.Owner, caster, skill, caster.Position, ForceId.GetNew(), null);
} else
{
skill.IncreaseOverheat();
}
}

// [Arts] Spirit Expert: Wandering Soul
if (casterCharacter is DummyCharacter dummyCharacter3 && dummyCharacter3.Owner.IsAbilityActive(AbilityId.Sadhu35))
{
this.RemoveDummyCharacter(dummyCharacter3);
return;
}

casterCharacter.Properties.Modify(PropertyName.MSPD_BM, -buff.NumArg1);

Send.ZC_NORMAL.EndOutOfBodyBuff(casterCharacter, BuffId.OOBE_Moksha_Buff);
Send.ZC_NORMAL.UpdateModelColor(casterCharacter, 255, 255, 255, 255, 0.01f);

Send.ZC_PLAY_SOUND(casterCharacter, "skl_eff_yuchae_end_2");

this.ReturnToBody(casterCharacter, (int)buff.NumArg2);
}

/// <summary>
/// Called in regular intervals while the pad is on a map.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void OnUpdate(object sender, PadTriggerArgs args)
{
var pad = args.Trigger;
var caster = args.Creator;
var skill = args.Skill;

var targets = pad.Trigger.GetAttackableEntities(caster);

// The explosion has its own maximum target count which is separate from the skill
var maxTargets = pad.Trigger.MaxActorCount;

if (ZoneServer.Instance.Conf.World.DisableSDR)
maxTargets = int.MaxValue;

foreach (var target in targets.LimitRandom(maxTargets))
{
this.Attack(skill, caster, target);
}
}

/// <summary>
/// Executes end attack when the pad ends.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void OnDestroyPad(object sender, PadTriggerArgs args)
{
var pad = args.Trigger;
var creator = args.Creator;

this.EndAttack(pad.Skill, creator, (ISplashArea)pad.Area);
}

/// <summary>
/// Attacks the target
/// </summary>
/// <param name="skill"></param>
/// <param name="caster"></param>
private void Attack(Skill skill, ICombatEntity caster, ICombatEntity target)
{
var skillHitResult = SCR_SkillHit(caster, target, skill);

target.TakeDamage(skillHitResult.Damage, caster);

var hit = new HitInfo(caster, target, skill, skillHitResult, TimeSpan.FromMilliseconds(0));

Send.ZC_HIT_INFO(caster, target, hit);
}

/// <summary>
/// Executes the end attack when the skill's pad ends
/// </summary>
/// <param name="skill"></param>
/// <param name="caster"></param>
/// <param name="splashArea"></param>
private void EndAttack(Skill skill, ICombatEntity caster, ISplashArea splashArea)
{
var damageDelay = TimeSpan.FromMilliseconds(50);
var skillHitDelay = TimeSpan.Zero;

var targets = caster.Map.GetAttackableEntitiesIn(caster, splashArea);

// The explosion has its own maximum target count which is separate from the skill
var maxTargets = 10;

if (ZoneServer.Instance.Conf.World.DisableSDR)
maxTargets = int.MaxValue;

foreach (var target in targets.LimitRandom(maxTargets))
{
var skillHitResult = SCR_SkillHit(caster, target, skill);

target.TakeDamage(skillHitResult.Damage, caster);

// 6 Consecutive hits instead of a single packet
for (int i = 0; i < 6; i++)
{
var hit = new HitInfo(caster, target, skill, skillHitResult, TimeSpan.FromMilliseconds(i * 150));
Send.ZC_HIT_INFO(caster, target, hit);
}
}
}
}
}
Loading