Skip to content

Commit

Permalink
Some cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
atravita-mods committed Jan 31, 2022
1 parent 8d91da8 commit 47df12c
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 19 deletions.
9 changes: 4 additions & 5 deletions SpecialOrdersExtended/DataModels/DialogueLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ public DialogueLog(string savefile, long multiplayerID)
: base(savefile) => this.multiplayerID = multiplayerID;

/// <summary>
/// Gets or sets backing field that contains all the SeenDialogues.
/// Gets backing field that contains all the SeenDialogues.
/// </summary>
public Dictionary<string, List<string>> SeenDialogues { get; set; } = new();
/// <remarks>Avoid using this directly; use the TryAdd/TryRemove/Contains methods instead if possible.</remarks>
public Dictionary<string, List<string>> SeenDialogues { get; private set; } = new();

/// <summary>
/// Loads a dialogueLog.
Expand All @@ -38,7 +39,7 @@ public static DialogueLog Load(long multiplayerID)
}
DialogueLog log = ModEntry.DataHelper.ReadGlobalData<DialogueLog>($"{Constants.SaveFolderName}{IDENTIFIER}{multiplayerID:X8}")
?? new DialogueLog(Constants.SaveFolderName, multiplayerID);
log.multiplayerID = multiplayerID;
log.multiplayerID = multiplayerID; // fix the multiplayer ID since ReadGlobalData will use the default zero-parameter constructor.
return log;
}

Expand Down Expand Up @@ -87,7 +88,6 @@ public static DialogueLog LoadTempIfAvailable(long multiplayerID)
/// <param name="dialoguekey">Exact dialogue key.</param>
/// <param name="characterName">Which character to check.</param>
/// <returns>True if found, false otheerwise.</returns>
[Pure]
public bool Contains(string dialoguekey, string characterName)
{
if (this.SeenDialogues.TryGetValue(dialoguekey, out List<string>? characterList))
Expand Down Expand Up @@ -139,7 +139,6 @@ public bool TryRemove(string dialoguekey, string characterName)
}

/// <inheritdoc/>
[Pure]
public override string ToString()
{
StringBuilder stringBuilder = new();
Expand Down
3 changes: 0 additions & 3 deletions SpecialOrdersExtended/DataModels/RecentCompletedSO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ public List<string> dayUpdate(uint daysPlayed)
/// <param name="days">Days to check.</param>
/// <returns>True if order found and completed in the last X days, false otherwise.</returns>
/// <remarks>Orders are removed from the list after seven days.</remarks>
[Pure]
public bool IsWithinXDays(string orderKey, uint days)
{
if (this.RecentOrdersCompleted.TryGetValue(orderKey, out uint dayCompleted))
Expand All @@ -133,15 +132,13 @@ public bool IsWithinXDays(string orderKey, uint days)
/// </summary>
/// <param name="days">Number of days to look at.</param>
/// <returns>IEnumerable of keys within the given timeframe.</returns>
[Pure]
public IEnumerable<string> GetKeys(uint days)
{
return this.RecentOrdersCompleted.Keys
.Where(a => this.RecentOrdersCompleted[a] + days >= Game1.stats.DaysPlayed);
}

/// <inheritdoc/>
[Pure]
public override string ToString()
{
StringBuilder stringBuilder = new();
Expand Down
14 changes: 11 additions & 3 deletions SpecialOrdersExtended/DialogueManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ public static void ConsoleSpecialOrderDialogue(string command, string[] args)
/// <param name="characterName">Name of character.</param>
/// <returns>True if key has been said, false otherwise.</returns>
/// <exception cref="SaveNotLoadedError">Save is not loaded.</exception>
[Pure]
public static bool HasSeenDialogue(string key, string characterName)
{
if (!Context.IsWorldReady)
Expand Down Expand Up @@ -359,10 +358,12 @@ public static void PushPossibleDelayedDialogues()
{
if (result.PushIfPastTime(Game1.timeOfDay))
{
// Successfully pushed, remove from queue.
_ = DelayedDialogues.Value.Dequeue();
}
else
{
// Everyone else should be behind me in time, so skip to next timeslot.
return;
}
}
Expand All @@ -381,13 +382,17 @@ private static bool PushAndSaveDialogue(string dialogueKey, NPC npc)
{// I have already said this dialogue
return false;
}

// Empty NPC's current dialogue stack and keep it in a queue for now.
while (npc.CurrentDialogue.TryPop(out Dialogue? result))
{
DelayedDialogues.Value.Enqueue(new DelayedDialogue(
time: Game1.timeOfDay + 100,
time: Game1.timeOfDay + 100, // delay by one hour.
npc: npc,
dialogue: result));
}

// Push my dialogue onto their stack.
npc.CurrentDialogue.Push(new Dialogue(npc.Dialogue[dialogueKey], npc) { removeOnNextMove = true });
if (ModEntry.Config.Verbose)
{
Expand Down Expand Up @@ -434,7 +439,10 @@ private static bool FindBestDialogue(string baseKey, NPC npc, int hearts)
}
}

ModEntry.ModMonitor.DebugLog(I18n.Dialogue_NoKey(baseKey, npc.Name), LogLevel.Trace);
if (ModEntry.Config.Verbose)
{
ModEntry.ModMonitor.Log(I18n.Dialogue_NoKey(baseKey, npc.Name), LogLevel.Trace);
}
return false;
}
}
43 changes: 43 additions & 0 deletions SpecialOrdersExtended/ModEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public override void Entry(IModHelper helper)
ModMonitor.Log($"Failed to patch NPC::checkForNewCurrentDialogue for Special Orders Dialogue. Dialogue will be disabled\n\n{ex}", LogLevel.Error);
}

// Register console commands.
helper.ConsoleCommands.Add(
name: "special_order_pool",
documentation: I18n.SpecialOrderPool_Description(),
Expand All @@ -108,23 +109,43 @@ public override void Entry(IModHelper helper)
name: "special_orders_dialogue",
documentation: $"{I18n.SpecialOrdersDialogue_Description()}\n\n{I18n.SpecialOrdersDialogue_Example()}\n {I18n.SpecialOrdersDialogue_Usage()}\n {I18n.SpecialOrdersDialogue_Save()}",
callback: DialogueManager.ConsoleSpecialOrderDialogue);

// Register event handlers.
helper.Events.GameLoop.GameLaunched += this.OnGameLaunched;
helper.Events.GameLoop.SaveLoaded += this.SaveLoaded;
helper.Events.GameLoop.Saving += this.Saving;
helper.Events.GameLoop.TimeChanged += this.OnTimeChanged;
helper.Events.GameLoop.OneSecondUpdateTicking += this.OneSecondUpdateTicking;
}

/// <summary>
/// Raised every 10 in game minutes.
/// </summary>
/// <param name="sender">Unknown, used by SMAPI.</param>
/// <param name="e">TimeChanged params.</param>
/// <remarks>Currently handles: pushing delayed dialogue back onto the stack.</remarks>
private void OnTimeChanged(object? sender, TimeChangedEventArgs e)
{
DialogueManager.PushPossibleDelayedDialogues();
}

/// <summary>
/// Raised every second.
/// </summary>
/// <param name="sender">Unknown, used by SMAPI.</param>
/// <param name="e">OneSecondUpdate params.</param>
/// <remarks>Currently handles: grabbing new recently completed special orders.</remarks>
private void OneSecondUpdateTicking(object? sender, OneSecondUpdateTickingEventArgs e)
{
RecentSOManager.GrabNewRecentlyCompletedOrders();
}

/// <summary>
/// Raised on game launch.
/// </summary>
/// <param name="sender">Unknown, used by SMAPI.</param>
/// <param name="e">Game Launched arguments.</param>
/// <remarks>Used to bind APIs and register CP tokens.</remarks>
private void OnGameLaunched(object? sender, GameLaunchedEventArgs e)
{
// Bind Spacecore API
Expand All @@ -148,6 +169,12 @@ private void OnGameLaunched(object? sender, GameLaunchedEventArgs e)
api.RegisterToken(this.ModManifest, "RecentCompleted", new Tokens.RecentCompletedSO());
}

/// <summary>
/// Raised right before the game is saved.
/// </summary>
/// <param name="sender">Unknown, used by SMAPI.</param>
/// <param name="e">Event arguments.</param>
/// <remarks>Used to handle day-end events.</remarks>
private void Saving(object? sender, SavingEventArgs e)
{
this.Monitor.DebugLog("Event Saving raised");
Expand All @@ -167,13 +194,24 @@ private void Saving(object? sender, SavingEventArgs e)
RecentSOManager.Save();
}

/// <summary>
/// Raised when save is loaded.
/// </summary>
/// <param name="sender">Unknown, used by SMAPI.</param>
/// <param name="e">Parameters.</param>
/// <remarks>Used to load in this mod's data models.</remarks>
private void SaveLoaded(object? sender, SaveLoadedEventArgs e)
{
this.Monitor.DebugLog("Event SaveLoaded raised");
DialogueManager.Load(Game1.player.UniqueMultiplayerID);
RecentSOManager.Load();
}

/// <summary>
/// Console commands to check the value of a tag.
/// </summary>
/// <param name="command">Name of the command.</param>
/// <param name="args">List of tags to check.</param>
private void ConsoleCheckTag(string command, string[] args)
{
if (!Context.IsWorldReady)
Expand All @@ -199,6 +237,11 @@ private void ConsoleCheckTag(string command, string[] args)
}
}

/// <summary>
/// Console command to get all available orders.
/// </summary>
/// <param name="command">Name of command.</param>
/// <param name="args">Arguments for command.</param>
private void GetAvailableOrders(string command, string[] args)
{
if (!Context.IsWorldReady)
Expand Down
2 changes: 0 additions & 2 deletions SpecialOrdersExtended/RecentSOManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ public static void SaveTemp()
/// </summary>
/// <param name="days">current number of days played.</param>
/// <returns>IEnumerable of keys within the given timeframe. May return null.</returns>
[Pure]
public static IEnumerable<string>? GetKeys(uint days) => recentCompletedSO?.GetKeys(days);

/// <summary>
Expand Down Expand Up @@ -177,7 +176,6 @@ public static bool TryRemove(string questkey)
/// <returns>True if questkey found and was completed in X days, false otherwise.</returns>
/// <exception cref="SaveNotLoadedError">Save not loaded.</exception>
/// <remarks>The data model will delete any entries older than 7 days, so beyond that it just won't know.</remarks>
[Pure]
public static bool IsWithinXDays(string questkey, uint days)
{
if (!Context.IsWorldReady)
Expand Down
3 changes: 0 additions & 3 deletions SpecialOrdersExtended/Tokens/AbstractToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,17 @@ internal abstract class AbstractToken
/// Whether or not the token allows input. Default, true.
/// </summary>
/// <returns>true - all derived tokens should allow input.</returns>
[Pure]
public virtual bool AllowsInput() => false;

/// <summary>
/// Whether or not the token will produce multiple outputs, depending on the input to the token.
/// </summary>
/// <param name="input">Input to token.</param>
/// <returns>False (no need for my own inputs).</returns>
[Pure]
public virtual bool CanHaveMultipleValues(string? input = null) => true;

/// <summary>Get whether the token is available for use.</summary>
/// <returns>True if token ready, false otherwise.</returns>
[Pure]
public virtual bool IsReady() => this.tokenCache is not null;

/// <summary>Validate that the provided input arguments are valid.</summary>
Expand Down
4 changes: 3 additions & 1 deletion SpecialOrdersExtended/docs/CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Changelog

1. Add condition to just randomly reduce the chance of a quest appearing. (May need integrate with that one mod that changes the day special orders appear?)
2. Add condition for total number of quests completed, per board.
<!-- Better documentation for the dialogue tags? -->

#### Stardew 1.6 todo

Expand All @@ -17,7 +18,8 @@ Changelog
#### Version 1.0.6

* Add verbosity config option. Intended mostly for mod authors.
* `week_X` now handles extended months (in theory.)
* Special Order Dialogue will now cause the NPC's original dialogue to be delayed by one in-game hour. (Previously, dialogue from this mod would just be added on top of the dialogue stack.)
* `week_X` now handles extended months.
* Add `profession` tag. Makes `skilllevel` tag take into account Spacecore skills, if that's installed (in theory. Memo to self: test that).
* For the `_IsAvailable` dialogue series, remove from possible pool if quest is already accepted.
* Fixes splitscreen, but for real this time.
Expand Down
4 changes: 3 additions & 1 deletion SpecialOrdersExtended/docs/DialogueKeys.MD
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ New in 1.0.2, there's dialogue keys. Special Order Dialogue Keys will only ever

Quest key is the key for the quest (as it is in the `SpecialOrders` file). Quest status is either `Completed` or `InProgress`. (The status `Failed` is technically also available, but I don't think you can actually talk to a NPC in that time.) `Completed` dialogue will persist to the end of the day after quest completion. Additionally, `IsAvailable` will reference quests currently available on the Quests board, and `RepeatOrder` can be used when the quest has been completed at least once before. `RepeatOrder` keys will also be cleared seven days after the successful completion of a quest.

Preface/predicate limits when the key can be used, and follows a similar structure as to vanilla's location keys. <Preface> can be any season, and season-specific keys will always take precedence. Predicate can be any of the following values, in priority order:
Preface/predicate limits when the key can be used, and follows a similar structure as to vanilla's location keys. `<Preface>` can be any season, and season-specific keys will always take precedence. Predicate can be any of the following values, in priority order:

1. `_<short day of week>`
2. `<heartlevel>` (where valid heart levels are even numbers, checked counting down - ie 14,12,10,8,6,4,2 in vanilla)
Expand All @@ -15,6 +15,8 @@ For example: if I have a quest key that's `atravita.ValleyFlowers` and I'm writi

If I wanted a bit of dialogue to be said while the quest is in progress, I would use `atravita.ValleyFlowers_InProgress`, which will only ever be said once while the quest is in progress. On the other hand, `atravita.ValleyFlowers_RepeatOrder` can only be said while the quest is in progress AND has been completed at least once before.

The NPC's original dialogue is held in a queue and re-added after an in-game hour.

### Storage

To prevent dialogue keys from this mod from becoming too spammy, they're only ever said once (except for `RepeatOrder` keys, but those have a cooldown period of seven days after the completion of the Special Order). As of 1.0.4+, previously seen keys are stored in the global mod data, locally (`.smapi/mod-data/atravita.specialordersextended`). The console command `special_orders_dialogue` is included to look at and and/remove from this data.
Expand Down
2 changes: 1 addition & 1 deletion SpecialOrdersExtended/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"$schema": "https://smapi.io/schemas/manifest.json",
"Name": "Special Orders Tags Extended",
"Author": "atravita",
"Version": "1.0.6-beta",
"Version": "1.0.6",
"Description": "Extends the number of tags available for special orders",
"UniqueID": "atravita.SpecialOrdersExtended",
"EntryDll": "SpecialOrdersExtended.dll",
Expand Down

0 comments on commit 47df12c

Please sign in to comment.