-
Notifications
You must be signed in to change notification settings - Fork 83
Add mastery commands for player management and debugging #562
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
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 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,68 @@ public PlayerCommand(GameSession session, AchievementMetadataStorage achievement | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddCommand(new CurrencyCommand(session)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddCommand(new InventoryCommand(session)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddCommand(new TrophyCommand(session, achievementMetadataStorage)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddCommand(new MasteryCommand(session)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private class MasteryCommand : Command { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public MasteryCommand(GameSession session) : base("mastery", "Set player mastery.") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddCommand(new MasteryExpCommand(session)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddCommand(new MasteryLevelCommand(session)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private class MasteryExpCommand : Command { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly GameSession session; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public MasteryExpCommand(GameSession session) : base("exp", "Set player mastery experience.") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.session = session; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var masteryCode = new Argument<MasteryType>("mastery", "MasteryType of the player."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var exp = new Argument<int>("exp", "Experience points to add."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddArgument(masteryCode); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddArgument(exp); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.SetHandler<InvocationContext, MasteryType, int>(Handle, masteryCode, exp); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void Handle(InvocationContext ctx, MasteryType masteryType, int exp) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| session.Mastery[masteryType] = exp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.ExitCode = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (SystemException ex) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.Console.Error.WriteLine(ex.Message); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.ExitCode = 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private class MasteryLevelCommand : Command { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private readonly GameSession session; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public MasteryLevelCommand(GameSession session) : base("level", "Set player mastery level.") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.session = session; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var masteryCode = new Argument<MasteryType>("mastery", "MasteryType of the player."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var level = new Argument<int>("level", "Level of the mastery."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddArgument(masteryCode); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AddArgument(level); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.SetHandler<InvocationContext, MasteryType, int>(Handle, masteryCode, level); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void Handle(InvocationContext ctx, MasteryType masteryType, int level) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int exp = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (session.TableMetadata.MasteryRewardTable.Entries.TryGetValue(masteryType, out IReadOnlyDictionary<int, MasteryRewardTable.Entry>? masteryRewardMetadata)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exp = masteryRewardMetadata.OrderByDescending(mastery => mastery.Key).FirstOrDefault(mastery => level >= mastery.Key).Value.Value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| session.Mastery[masteryType] = exp; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.ExitCode = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+73
to
+80
Contributor
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. Avoid NullReference when no threshold ≤ level; handle missing metadata and invalid levels. If there’s no mastery grade ≤ requested level, FirstOrDefault returns a default KVP and Apply this diff to make the computation robust and user-friendly: private void Handle(InvocationContext ctx, MasteryType masteryType, int level) {
try {
- int exp = 0;
- if (session.TableMetadata.MasteryRewardTable.Entries.TryGetValue(masteryType, out IReadOnlyDictionary<int, MasteryRewardTable.Entry>? masteryRewardMetadata)) {
- exp = masteryRewardMetadata.OrderByDescending(mastery => mastery.Key).FirstOrDefault(mastery => level >= mastery.Key).Value.Value;
- }
- session.Mastery[masteryType] = exp;
- ctx.ExitCode = 0;
+ if (!session.TableMetadata.MasteryRewardTable.Entries.TryGetValue(masteryType, out IReadOnlyDictionary<int, MasteryRewardTable.Entry>? masteryRewardMetadata)) {
+ ctx.Console.Error.WriteLine($"Mastery metadata not found for {masteryType}.");
+ ctx.ExitCode = 1;
+ return;
+ }
+
+ if (level < 1) {
+ ctx.Console.Error.WriteLine($"Invalid mastery level: {level}. Must be >= 1.");
+ ctx.ExitCode = 1;
+ return;
+ }
+
+ // Highest grade threshold <= requested level
+ int applicableGrade = masteryRewardMetadata.Keys.Where(k => k <= level).DefaultIfEmpty(-1).Max();
+ if (applicableGrade < 0 || !masteryRewardMetadata.TryGetValue(applicableGrade, out var entry)) {
+ ctx.Console.Error.WriteLine($"No mastery grade threshold found for level {level} ({masteryType}).");
+ ctx.ExitCode = 1;
+ return;
+ }
+
+ session.Mastery[masteryType] = entry.Value;
+ ctx.ExitCode = 0;
} catch (SystemException ex) {
ctx.Console.Error.WriteLine(ex.Message);
ctx.ExitCode = 1;
}
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (SystemException ex) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.Console.Error.WriteLine(ex.Message); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ctx.ExitCode = 1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private class LevelCommand : Command { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.
💡 Verification agent
🧩 Analysis chain
Verify MasteryType names vs PR examples; consider documenting accepted values or mapping synonyms.
Your PR text lists “Music, Gathering, Breeding, Engraving” but Model/Mastery properties use “Instrument, Foraging, Ranching, Handicrafts.” If the enum uses the latter names, System.CommandLine will reject the former. Either:
Run this to confirm the enum members used at runtime:
If synonyms are needed and you want a lightweight change, I can propose a small mapper without touching other callsites. Want me to draft it?
Also applies to: 65-71
🏁 Script executed:
Length of output: 143
🏁 Script executed:
Length of output: 21794
🏁 Script executed:
Length of output: 451
Document or Align MasteryType Inputs
Please ensure that the command-line examples and handlers use the exact enum identifiers defined in
Maple2.Model.Enum.MasteryType(Unknown, Fishing, Music, Mining, Gathering, Breeding, Farming, Blacksmithing, Engraving, Alchemist, Cooking, PetTaming). Currently, System.CommandLine will only accept those names (e.g. “Music”) and numeric values, not the game-facing synonyms (“Instrument”, “Foraging”, “Ranching”, “Handicrafts”).• Update your PR’s documentation/examples in
to list the actual enum values, for instance:
• If you’d like to support synonyms, replace the raw
Argument<MasteryType>withArgument<string>(or keep both) and map input strings to the correct enum before handling. For example:Let me know if you’d like a fuller PR to add this mapper.
🤖 Prompt for AI Agents