Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions doc/bt/ZC_NORMAL.bt
Original file line number Diff line number Diff line change
Expand Up @@ -1715,6 +1715,25 @@ switch (type)
int i[count];
break;

// ? (Sent while counter attacking with Matar skill)
// Force Cast Skill?
case 0x1A3:
int CasterHandle;
int skillId;
float f1;
short s1;
short s2;
short s3;
short s4;
short s5;
short s6;
short s7;
byte b1;
short s8;
int i4;
float f2;
break;

// Related to teleport skill?
// Also sent on ? (Sent on assister dungeon)
case 0x1A6:
Expand Down
14 changes: 12 additions & 2 deletions src/Shared/Data/Database/SkillTree.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Melia.Shared.Game.Const;
using Newtonsoft.Json.Linq;
Expand All @@ -21,8 +22,7 @@ public class SkillTreeData
public class SkillTreeDb : DatabaseJson<SkillTreeData>
{
/// <summary>
/// Returns all skills the given job can learn at a certain job
/// level.
/// Returns all skills the given job can learn at a certain job level.
/// </summary>
/// <param name="jobId"></param>
/// <param name="circle"></param>
Expand All @@ -32,6 +32,16 @@ public SkillTreeData[] FindSkills(JobId jobId, int jobLevel)
return this.Entries.Where(a => a.JobId == jobId && a.UnlockLevel <= jobLevel).ToArray();
}

/// <summary>
/// Returns a list of SkillIds that the given job can learn.
/// </summary>
/// <param name="jobId"></param>
/// <returns></returns>
public List<SkillId> FindSkillIds(JobId jobId)
{
return this.Entries.Where(a => a.JobId == jobId).Select(a => a.SkillId).ToList();
}

/// <summary>
/// Reads given entry and adds it to the database.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions src/Shared/Network/NormalOp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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 All @@ -57,6 +58,7 @@ public static class Zone
public const int Cutscene = 0x6B;
public const int SetSkillSpeed = 0x77;
public const int SetHitDelay = 0x78;
public const int FloorEffect = 0x81;
public const int SkillCancelCancel = 0x7D;
public const int SpinObject = 0x8A;
public const int OpenBook = 0x9E;
Expand All @@ -79,6 +81,7 @@ public static class Zone
public const int UpdateSkillUI = 0x189;
public const int AdventureBook = 0x197;
public const int Unknown_19B = 0x19E;
public const int ForceClientCastSkill = 0x1A3;
public const int PadSetModel = 0x1AB;
public const int WigVisibilityUpdate = 0x1AC;
public const int Unknown_1B4 = 0x1B7;
Expand Down
2 changes: 1 addition & 1 deletion src/ZoneServer/Abilities/AbilityHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void registerDefenseFunc(string name, CombatCalcHookFunction func)
if (handler is IAbilityCombatAttackBeforeCalcHandler beforeCalcAttackHandler) registerAttackFunc("SCR_Combat_BeforeCalc_Attack_" + abilityId, beforeCalcAttackHandler.OnAttackBeforeCalc);
if (handler is IAbilityCombatDefenseBeforeCalcHandler beforeCalcDefenseHandler) registerDefenseFunc("SCR_Combat_BeforeCalc_Defense_" + abilityId, beforeCalcDefenseHandler.OnDefenseBeforeCalc);

if (handler is IAbilityCombatAttackAfterCalcHandler afterCalcAttackHandler) registerAttackFunc("SCR_Combat_AfterCalc_Attack_" + abilityId, afterCalcAttackHandler.OnAttackAfterCalc);
if (handler is IAbilityCombatAttackAfterCalcHandler afterCalcAttackHandler) registerAttackFunc("SCR_Combat_AfterCalc_Attack_Abillity_" + abilityId, afterCalcAttackHandler.OnAttackAfterCalc);
if (handler is IAbilityCombatDefenseAfterCalcHandler afterCalcDefenseHandler) registerDefenseFunc("SCR_Combat_AfterCalc_Defense_" + abilityId, afterCalcDefenseHandler.OnDefenseAfterCalc);

if (handler is IAbilityCombatAttackBeforeBonusesHandler beforeBonusesAttackHandler) registerAttackFunc("SCR_Combat_BeforeBonuses_Attack_" + abilityId, beforeBonusesAttackHandler.OnAttackBeforeBonuses);
Expand Down
3 changes: 3 additions & 0 deletions src/ZoneServer/Buffs/Base/BuffHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using Melia.Shared.Game.Const;
using Melia.Zone.Scripting;
using Melia.Zone.World;
using Melia.Zone.World.Actors;
using Melia.Zone.World.Actors.Monsters;

namespace Melia.Zone.Buffs.Base
{
Expand Down
43 changes: 43 additions & 0 deletions src/ZoneServer/Buffs/Handlers/Swordsmen/Matador/Capote_Debuff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Melia.Shared.Game.Const;
using Melia.Zone.Buffs.Base;
using Melia.Zone.Network;
using Melia.Zone.Scripting.AI;
using Melia.Zone.World.Actors.CombatEntities.Components;

namespace Melia.Zone.Buffs.Handlers.Swordsmen.Matador
{
/// <summary>
/// Handle for the Capote Debuff, which increases the thread from the mob to the caster
/// </summary>
[BuffHandler(BuffId.Capote_Debuff)]
public class Capote_Debuff : BuffHandler
{
private const float DecreaseCrtResRate = -0.15f;

public override void OnActivate(Buff buff, ActivationType activationType)
{
var target = buff.Target;
var caster = buff.Caster;

Send.ZC_NORMAL.UpdateModelColor(target, 150, 160, 255, 255, 1.5f);

AddPropertyModifier(buff, target, PropertyName.CRTDR_BM, DecreaseCrtResRate);

if (target.Components.TryGet<AiComponent>(out var component))
component.Script.QueueEventAlert(new TauntEventAlert(target, caster));
}

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

Send.ZC_NORMAL.UpdateModelColor(target, 255, 255, 255, 255, 1);

RemovePropertyModifier(buff, target, PropertyName.CRTDR_BM);

if (target.Components.TryGet<AiComponent>(out var component))
component.Script.QueueEventAlert(new ResetTauntHateEventAlert(target, caster));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using Melia.Shared.Data.Database;
using Melia.Shared.Game.Const;
using Melia.Zone.Buffs.Base;
using Melia.Zone.Network;
using Melia.Zone.Skills;
using Melia.Zone.Skills.Combat;
using Melia.Zone.World.Actors;
using Melia.Zone.World.Actors.Characters;

namespace Melia.Zone.Buffs.Handlers.Swordsmen.Matador
{
/// <summary>
/// Handle for the Muleta Cast Buff, which makes the character immune to some type of attacks.
/// </summary>
[BuffHandler(BuffId.Muleta_Cast_Buff)]
public class Muleta_Cast_Buff : BuffHandler, IBuffCombatDefenseBeforeCalcHandler
{
/// <summary>
/// Makes the character immune to some type of physical attacks,
/// Also can decrease cooldown for Matador skills on hits.
/// <param name="buff"></param>
/// <param name="attacker"></param>
/// <param name="target"></param>
/// <param name="skill"></param>
/// <param name="modifier"></param>
/// <param name="skillHitResult"></param>
public void OnDefenseBeforeCalc(Buff buff, ICombatEntity attacker, ICombatEntity target, Skill skill, SkillModifier modifier, SkillHitResult skillHitResult)
{
if (!target.IsBuffActive(BuffId.Muleta_Cast_Buff))
return;

// Melee physical attacks ignored when counter-attacking (Matador).
if (skill.Data.AttackType == SkillAttackType.Slash || skill.Data.AttackType == SkillAttackType.Strike || skill.Data.AttackType == SkillAttackType.Melee)
{
// Muleta: Counterattack Master
if (attacker.Race == RaceType.Widling || target.IsAbilityActive(AbilityId.Matador8))
{
target.StopBuff(BuffId.IS_Channeling_Buff);

if (target is Character targetCharacter)
{
targetCharacter.TurnTowards(attacker);

// [Arts] Muleta: Faena
// Casts Faena instead of default attack when counter-attacking
if (targetCharacter.IsAbilityActive(AbilityId.Matador26))
Send.ZC_NORMAL.ForceClientCastSkill(targetCharacter, SkillId.Matador_Muleta, SkillId.Matador_Muleta_Faena);
else
Send.ZC_NORMAL.ForceClientCastSkill(targetCharacter, SkillId.Matador_Muleta, SkillId.Muleta_Attack);

// Muleta: Showtime
if (targetCharacter.IsAbilityActive(AbilityId.Matador16) && targetCharacter.TryGetAbility(AbilityId.Matador16, out var ability))
{
var reductionInSeconds = ability.Level;
var matadorSkillTreeDataList = ZoneServer.Instance.Data.SkillTreeDb.FindSkillIds(JobId.Matador);

foreach (var charSkill in targetCharacter.Skills.GetList())
{
if (charSkill.Id == SkillId.Matador_Muleta)
continue;

if (matadorSkillTreeDataList.Contains(charSkill.Id) && charSkill.IsOnCooldown)
charSkill.ReduceCooldown(TimeSpan.FromSeconds(reductionInSeconds));
}
}
}
}

modifier.FinalDamageMultiplier *= 0;
}
}
}
}
32 changes: 32 additions & 0 deletions src/ZoneServer/Buffs/Handlers/Swordsmen/Matador/Ole_Debuff.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Melia.Shared.Game.Const;
using Melia.Zone.Buffs.Base;
using Melia.Zone.Scripting.AI;
using Melia.Zone.World.Actors.CombatEntities.Components;

namespace Melia.Zone.Buffs.Handlers.Swordsmen.Matador
{
/// <summary>
/// Handle for the Ole Debuff, which increases the thread from the mob to the caster
/// </summary>
[BuffHandler(BuffId.Ole_Debuff)]
public class Ole_Debuff : BuffHandler
{
public override void OnActivate(Buff buff, ActivationType activationType)
{
var target = buff.Target;
var caster = buff.Caster;

if (target.Components.TryGet<AiComponent>(out var component))
component.Script.QueueEventAlert(new TauntEventAlert(target, caster));
}

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

if (target.Components.TryGet<AiComponent>(out var component))
component.Script.QueueEventAlert(new ResetTauntHateEventAlert(target, caster));
}
}
}
73 changes: 72 additions & 1 deletion src/ZoneServer/Network/Send.Normal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1282,7 +1282,6 @@ public static void UnlockCollection(Character character, int collectionId)
character.Connection.Send(packet);
}


/// <summary>
/// Updates the collection for the player.
/// </summary>
Expand All @@ -1299,6 +1298,77 @@ public static void UpdateCollection(Character character, int collectionId, int i
character.Connection.Send(packet);
}

/// <summary>
/// Updates the entity model color
/// </summary>
/// <param name="entity"></param>
/// <param name="red"></param>
/// <param name="green"></param>
/// <param name="blue"></param>
/// <param name="alpha"></param>
/// <param name="f1"></param>
public static void UpdateModelColor(ICombatEntity entity, int red, int green, int blue, int alpha, float f1)
{
var packet = new Packet(Op.ZC_NORMAL);
packet.PutInt(NormalOp.Zone.UpdateModelColor);

packet.PutInt(entity.Handle);
packet.PutByte((byte)red);
packet.PutByte((byte)green);
packet.PutByte((byte)blue);
packet.PutByte((byte)alpha);
packet.PutByte(1);
packet.PutFloat(f1);
packet.PutByte(1);

entity.Map.Broadcast(packet);
}

/// <summary>
/// Seems to force the client to cast a skill (used on Matador's Muleta skill)
/// </summary>
/// <param name="character"></param>
/// <param name="skillId"></param>
/// <param name="castSkillId"></param>
public static void ForceClientCastSkill(Character character, SkillId beforeSkillId, SkillId castSkillId)
{
var packet = new Packet(Op.ZC_NORMAL);
packet.PutInt(NormalOp.Zone.ForceClientCastSkill);

packet.PutInt(character.Handle);
packet.PutInt((int)beforeSkillId);
packet.PutFloat(0.515625f);
packet.PutShort(-32255);
packet.PutShort(164);
packet.PutShort(0);
packet.PutShort(2368);
packet.PutShort(68);
packet.PutShort(22096);
packet.PutShort(62);
packet.PutByte(0xA0);
packet.PutShort(-15173);
packet.PutInt((int)castSkillId);
packet.PutFloat(1000f);

character.Connection.Send(packet);
}

/// <summary>
/// Unknow purposes, used by Matador skill Back Slide
/// </summary>
/// <param name="character"></param>
/// <param name="skillId"></param>
public static void FloorEffect(Character character, SkillId skillId)
{
var packet = new Packet(Op.ZC_NORMAL);
packet.PutInt(NormalOp.Zone.FloorEffect);

packet.PutInt(character.Handle);
packet.PutInt((int)skillId);

character.Map.Broadcast(packet);
}

/// <summary>
/// Exact purpose unknown, used in some skills when there's no target.
/// </summary>
Expand Down Expand Up @@ -1343,6 +1413,7 @@ public static void Skill_43(IActor actor)
actor.Map.Broadcast(packet);
}

/// <summary>
/// Opens book for the player.
/// </summary>
/// <param name="character"></param>
Expand Down
Loading