diff --git a/Maple2.Server.Game/Commands/PlayerCommand.cs b/Maple2.Server.Game/Commands/PlayerCommand.cs index d8d3889a8..6fe44f7eb 100644 --- a/Maple2.Server.Game/Commands/PlayerCommand.cs +++ b/Maple2.Server.Game/Commands/PlayerCommand.cs @@ -27,14 +27,15 @@ public PlayerCommand(GameSession session, AchievementMetadataStorage achievement private class MasteryCommand : Command { public MasteryCommand(GameSession session) : base("mastery", "Set player mastery.") { - AddCommand(new MasteryExpCommand(session)); + AddCommand(new MasteryAddExpCommand(session)); + AddCommand(new MasterySetExpCommand(session)); AddCommand(new MasteryLevelCommand(session)); } - private class MasteryExpCommand : Command { + private class MasteryAddExpCommand : Command { private readonly GameSession session; - public MasteryExpCommand(GameSession session) : base("exp", "Set player mastery experience.") { + public MasteryAddExpCommand(GameSession session) : base("addexp", "Add player mastery experience.") { this.session = session; var masteryCode = new Argument("mastery", "MasteryType of the player."); @@ -45,6 +46,31 @@ public MasteryExpCommand(GameSession session) : base("exp", "Set player mastery this.SetHandler(Handle, masteryCode, exp); } + private void Handle(InvocationContext ctx, MasteryType masteryType, int exp) { + try { + session.Mastery[masteryType] = session.Mastery[masteryType] + exp; + ctx.ExitCode = 0; + } catch (SystemException ex) { + ctx.Console.Error.WriteLine(ex.Message); + ctx.ExitCode = 1; + } + } + } + + private class MasterySetExpCommand : Command { + private readonly GameSession session; + + public MasterySetExpCommand(GameSession session) : base("setexp", "Set player mastery experience.") { + this.session = session; + + var masteryCode = new Argument("mastery", "MasteryType of the player."); + var exp = new Argument("exp", "Experience points to set to."); + + AddArgument(masteryCode); + AddArgument(exp); + this.SetHandler(Handle, masteryCode, exp); + } + private void Handle(InvocationContext ctx, MasteryType masteryType, int exp) { try { session.Mastery[masteryType] = exp; diff --git a/Maple2.Server.Game/Manager/AchievementManager.cs b/Maple2.Server.Game/Manager/AchievementManager.cs index f6327c1e2..72e184367 100644 --- a/Maple2.Server.Game/Manager/AchievementManager.cs +++ b/Maple2.Server.Game/Manager/AchievementManager.cs @@ -166,6 +166,18 @@ private void GiveReward(Achievement achievement, bool manualClaim = false) { return; } + bool hasMoreGrades = achievement.Metadata.Grades.Count > achievement.CurrentGrade; + // If an achievement has still more grade then we need to make sure the reward grade + // does not exceed the current grade. Else it will not show the correct trophy in + // the UI but rather the one past. + if (grade.Reward == null && hasMoreGrades) { + achievement.RewardGrade = Math.Min(achievement.RewardGrade + 1, achievement.CurrentGrade); + return; + } + + // If an achievement has no reward and no further grade we need to push the reward grade + // past the current grade to mark it as fully completed. Else it will not show the crown + // and completion date for the trophy but rather the claim button which is not correct. if (grade.Reward == null) { achievement.RewardGrade++; return; diff --git a/Maple2.Server.Game/Manager/MasteryManager.cs b/Maple2.Server.Game/Manager/MasteryManager.cs index b71c01d25..b0b154b42 100644 --- a/Maple2.Server.Game/Manager/MasteryManager.cs +++ b/Maple2.Server.Game/Manager/MasteryManager.cs @@ -9,6 +9,7 @@ using Maple2.Server.Game.Model; using Maple2.Server.Game.Packets; using Maple2.Server.Game.Session; +using Serilog; namespace Maple2.Server.Game.Manager; @@ -80,21 +81,60 @@ public int this[MasteryType type] { session.Send(MasteryPacket.UpdateMastery(type, session.Mastery[type])); int currentLevel = GetLevel(type); - if (startLevel < currentLevel || startValue == 0) { - if (type == MasteryType.Fishing) { - session.ConditionUpdate(ConditionType.fisher_grade, codeLong: currentLevel); - } else { - session.ConditionUpdate(ConditionType.mastery_grade, codeLong: (int) type); - } - } - if (startLevel > currentLevel) { - session.ConditionUpdate(ConditionType.set_mastery_grade, codeLong: (int) type); - if (type == MasteryType.Music) { - session.ConditionUpdate(ConditionType.music_play_grade); - } - } + int deltaLevel = currentLevel - startLevel + (startValue == 0 ? 1 : 0); + int deltaExp = value - startValue; + Log.Logger.Debug("[Mastery] {type} changed from {startValue} to {value} (Level {startLevel} -> {currentLevel}), ΔLevel: {deltaLevel}, ΔExp: {deltaExp}", type, startValue, value, startLevel, currentLevel, deltaLevel, deltaExp); + + HandleMasteryLevelChange(type, currentLevel, deltaLevel); + HandleMasteryExpIncrease(type, deltaExp); + } + + } + + /// + /// Handles the change of mastery level for a specified . + /// Updates the corresponding condition based on the mastery type and level changes. + /// + /// The type of mastery whos level has been increased. + /// The new current level of the mastery after the increase. + /// The delta by which the mastery level has changed. + private void HandleMasteryLevelChange(MasteryType type, int currentLevel, int deltaLevel) { + if (deltaLevel == 0) { + return; } + if (deltaLevel < 0) { + session.ConditionUpdate(ConditionType.set_mastery_grade, codeLong: (int) type); + return; + } + + switch (type) { + case MasteryType.Fishing: + session.ConditionUpdate(ConditionType.fisher_grade, codeLong: currentLevel); + return; + case MasteryType.Music: + session.ConditionUpdate(ConditionType.music_play_grade, counter: deltaLevel); + return; + default: + session.ConditionUpdate(ConditionType.mastery_grade, codeLong: (int) type); + return; + } + } + + /// + /// Handles the increase of mastery experience for a specified MasteryType. + /// Updates relevant conditions based on the mastery type and the amount of experience gained. + /// + /// The type of mastery for which experience is being increased. + /// The amount of experience that has been gained for the mastery. + private void HandleMasteryExpIncrease(MasteryType type, int deltaExp) { + switch (type) { + case MasteryType.Music: + session.ConditionUpdate(ConditionType.music_play_instrument_mastery, counter: deltaExp, codeLong: session.Instrument?.Value.Category ?? 0); + return; + default: + return; + } } public short GetLevel(MasteryType type) { diff --git a/Maple2.Server.Game/Util/ConditionUtil.cs b/Maple2.Server.Game/Util/ConditionUtil.cs index fb753fb6c..580cde349 100644 --- a/Maple2.Server.Game/Util/ConditionUtil.cs +++ b/Maple2.Server.Game/Util/ConditionUtil.cs @@ -110,6 +110,7 @@ private static bool CheckCode(this ConditionMetadata.Parameters code, GameSessio case ConditionType.holdtime: case ConditionType.riding: case ConditionType.fish_big: + case ConditionType.music_play_instrument_mastery: case ConditionType.music_play_instrument_time: case ConditionType.music_play_ensemble_in: case ConditionType.music_play_score: @@ -271,6 +272,7 @@ private static bool CheckTarget(this ConditionMetadata.Parameters target, GameSe case ConditionType.set_mastery_grade: case ConditionType.music_play_grade: case ConditionType.music_play_ensemble: + case ConditionType.music_play_instrument_mastery: case ConditionType.item_add: case ConditionType.item_pickup: case ConditionType.item_destroy: