/// <summary>A Harmony postfix patch that excludes a list of NPCs from <see cref="ItemDeliveryQuest"/>.</summary> /// <param name="__result">A list of NPCs this quest type could target.</param> public static void ItemDeliveryQuest_GetValidTargetList(ref List <NPC> __result) { try { List <string> excluded = new List <string>(); //a record of NPCs excluded during this process Dictionary <string, List <string> > exclusions = ModEntry.GetAllNPCExclusions(); //get all exclusion data for (int x = __result.Count - 1; x >= 0; x--) //for each valid NPC returned by the original method (looping backward to allow removal) { if (exclusions.ContainsKey(__result[x].Name)) //if this NPC has exclusion data { if (exclusions[__result[x].Name].Exists(entry => entry.StartsWith("All", StringComparison.OrdinalIgnoreCase) || //if this NPC is excluded from everything entry.StartsWith("TownQuest", StringComparison.OrdinalIgnoreCase) || //OR if this NPC is excluded from town quests entry.StartsWith("ItemDelivery", StringComparison.OrdinalIgnoreCase) //OR if this NPC is excluded from item delivery quests )) { excluded.Add(__result[x].Name); //add this NPC to the record __result.RemoveAt(x); //remove this NPC from the original results } } } if (excluded.Count > 0 && ModEntry.Instance.Monitor.IsVerbose) //if any NPCs were excluded { ModEntry.Instance.Monitor.Log($"Excluded NPCs from item delivery quest: {String.Join(", ", excluded)}", LogLevel.Trace); } } catch (Exception ex) { ModEntry.Instance.Monitor.LogOnce($"Harmony patch \"{nameof(ItemDeliveryQuest_GetValidTargetList)}\" has encountered an error and may revert to default behavior. Full error message:\n{ex.ToString()}", LogLevel.Error); } }
/// <summary>Gets a random NPC from <see cref="Utility.getRandomTownNPC(Random)"/> while removing any NPCs who are excluded from random town-related shop dialog.</summary> /// <returns>An random NPC from the Town who is NOT excluded from random town-related shop dialog.</returns> public static NPC GetRandomTownNPC_ShopDialogExclusions() { List <string> excluded = new List <string>(); //a list of NPC names to exclude from giving or receiving gifts foreach (KeyValuePair <string, List <string> > data in ModEntry.GetAllNPCExclusions()) //for each NPC's set of exclusion data { if (data.Value.Exists(entry => entry.StartsWith("All", StringComparison.OrdinalIgnoreCase) || //if this NPC is excluded from everything entry.StartsWith("TownEvent", StringComparison.OrdinalIgnoreCase) || //OR if this NPC is excluded from town events entry.StartsWith("ShopDialog", StringComparison.OrdinalIgnoreCase) //OR this NPC is excluded from the Winter Star event )) { excluded.Add(data.Key); //add this NPC's name to the excluded list } } HashSet <string> rerolledNames = new HashSet <string>(); //a record of NPCs excluded below NPC npc = Utility.getRandomTownNPC(); //get a random NPC while (excluded.Contains(npc?.Name, StringComparer.OrdinalIgnoreCase)) //while the selected NPC is NOT in the excluded list { rerolledNames.Add(npc?.Name); //add NPC name to record npc = Utility.getRandomTownNPC(); //get another random NPC } if (rerolledNames.Count > 0) //if any NPCs were excluded { string logMessage = String.Join(", ", rerolledNames); ModEntry.Instance.Monitor.Log($"Excluded NPCs from random shop dialog: {logMessage}", LogLevel.Trace); } return(npc); }
[EventPriority(EventPriority.Low)] //use low priority to run after most asset updates private static void DayStarted_SetupIslandSchedules(object sender, StardewModdingAPI.Events.DayStartedEventArgs e) { if (!Context.IsMainPlayer) //if this is NOT the main player { return; //do nothing } ExcludedVisitors = new HashSet <string>(StringComparer.OrdinalIgnoreCase); //create a new case-insensitive set and enable scheduling foreach (KeyValuePair <string, List <string> > data in ModEntry.GetAllNPCExclusions(forceCacheUpdate: true)) //for each NPC's set of exclusion data { if (data.Value.Exists(entry => entry.StartsWith("All", StringComparison.OrdinalIgnoreCase) || //if this NPC is excluded from everything entry.StartsWith("IslandEvent", StringComparison.OrdinalIgnoreCase) || //OR if this NPC is excluded from island events entry.StartsWith("IslandVisit", StringComparison.OrdinalIgnoreCase) //OR if this NPC is excluded from visting the island resort )) { ExcludedVisitors.Add(data.Key); //add this NPC's name to the excluded set } } if (ExcludedVisitors.Count > 0 && ModEntry.Instance.Monitor.IsVerbose) //if any NPCs were excluded { string logMessage = string.Join(", ", ExcludedVisitors); ModEntry.Instance.Monitor.Log($"Excluded NPCs from possible island visit: {logMessage}", LogLevel.Trace); } IslandSouth.SetupIslandSchedules(); //set up visitors' schedules ExcludedVisitors = null; //clear the set and disable scheduling }
/// <summary>Removes all entries from a dictionary where the key equals an excluded NPC's name.</summary> /// <param name="dispositions">A dictionary of data, generally loaded from Stardew's "Data/NPCDispositions" asset.</param> /// <returns>A copy of the dictionary with any excluded NPCs' entries removed.</returns> public static Dictionary <string, string> ExcludeFromNPCDispositions(Dictionary <string, string> dispositions) { if (dispositions == null) //if NPCDispositions is null for some reason { return(dispositions); //return the original asset } try { Dictionary <string, string> dispositionsWithExclusions = new Dictionary <string, string>(dispositions); //copy the dispositions to avoid editing the game's cached version List <string> excluded = new List <string>(); //a list of NPC names to exclude from the dispositions foreach (KeyValuePair <string, List <string> > data in ModEntry.GetAllNPCExclusions()) //for each NPC's set of exclusion data { if (data.Value.Exists(entry => entry.StartsWith("All", StringComparison.OrdinalIgnoreCase) || //if this NPC is excluded from everything entry.StartsWith("OtherEvent", StringComparison.OrdinalIgnoreCase) || //OR if this NPC is excluded from other events entry.StartsWith("PerfectFriend", StringComparison.OrdinalIgnoreCase) //OR if this NPC is excluded from the perfection system's friendship percentage )) { excluded.Add(data.Key); //add this NPC's name to the excluded list } } for (int x = excluded.Count - 1; x >= 0; x--) //for each excluded NPC (looping backward to allow removal) { string matchingDispositionsKey = dispositionsWithExclusions.Keys.FirstOrDefault(key => key.Equals(excluded[x], StringComparison.OrdinalIgnoreCase)); //search for a dispositions key that matches the excluded NPC's name if (matchingDispositionsKey != null) //if a key was found for this NPC { dispositionsWithExclusions.Remove(matchingDispositionsKey); //remove it from the dispositions } else //if a key was NOT found for this NPC { excluded.RemoveAt(x); //remove the excluded NPC from the list (for use in log messages) } } if (excluded.Count > 0) //if any NPCs were excluded { string logMessage = string.Join(", ", excluded); ModEntry.Instance.Monitor.Log($"Excluded NPCs from perfect friendship tracking: {logMessage}", LogLevel.Trace); } return(dispositionsWithExclusions); //return the filtered dispositions } catch (Exception ex) { ModEntry.Instance.Monitor.LogOnce($"Harmony patch \"{nameof(HarmonyPatch_PerfectionFriendship)}\" has encountered an error. Method \"{nameof(ExcludeFromNPCDispositions)}\" might not remove excluded NPCs. Full error message:\n{ex.ToString()}", LogLevel.Error); return(dispositions); //return the original asset } }
/// <summary>A Harmony postfix patch that excludes a list of NPCs from <see cref="SocializeQuest"/>.</summary> /// <param name="__instance">This instance of <see cref="SocializeQuest"/>.</param> public static void SocializeQuest_loadQuestInfo(SocializeQuest __instance) { try { List <string> excluded = new List <string>(); //a record of NPCs excluded during this process Dictionary <string, List <string> > exclusions = ModEntry.GetAllNPCExclusions(); //get all exclusion data for (int x = __instance.whoToGreet.Count - 1; x >= 0; x--) //for each NPC name selected by the original method (looping backward to allow removal) { if (exclusions.ContainsKey(__instance.whoToGreet[x])) //if this NPC has exclusion data { if (exclusions[__instance.whoToGreet[x]].Exists(entry => entry.StartsWith("All", StringComparison.OrdinalIgnoreCase) || //if this NPC is excluded from everything entry.StartsWith("TownQuest", StringComparison.OrdinalIgnoreCase) || //OR if this NPC is excluded from town quests entry.StartsWith("Socialize", StringComparison.OrdinalIgnoreCase) //OR if this NPC is excluded from socialize quests )) { excluded.Add(__instance.whoToGreet[x]); //add this NPC to the record __instance.whoToGreet.RemoveAt(x); //remove this NPC from the greeting list } } } if (excluded.Count > 0) //if any NPCs were excluded { __instance.total.Value -= excluded.Count; //subtract the removed NPCs from the quest's total __instance.objective.Value.param[1] = __instance.total.Value; //update the displayed total if (ModEntry.Instance.Monitor.IsVerbose) { ModEntry.Instance.Monitor.Log($"Excluded NPCs from socialize quest: {String.Join(", ", excluded)}", LogLevel.Trace); } } } catch (Exception ex) { ModEntry.Instance.Monitor.LogOnce($"Harmony patch \"{nameof(SocializeQuest_loadQuestInfo)}\" has encountered an error and may revert to default behavior. Full error message:\n{ex.ToString()}", LogLevel.Error); } }
/// <summary>A Harmony postfix patch that excludes a list of NPCs from greeting and/or being greeted by other NPCs.</summary> /// <param name="__instance">The NPC saying the greeting.</param> /// <param name="c">The character being greeted.</param> public static bool NPC_sayHiTo(NPC __instance, Character c) { try { Dictionary <string, List <string> > exclusions = ModEntry.GetAllNPCExclusions(); //get all exclusion data string greeterName = __instance.Name; //get the greeter's name (NPC performing the greeting) string recipientName = c?.Name; //get the recipient's name (character being greeted) bool SkipReply = false; //true if the recipient should NOT respond to the greeting if (greeterName != null && exclusions.TryGetValue(greeterName, out List <string> greeterExclusions)) //if the greeter has exclusion data { if (greeterExclusions.Exists(entry => entry.StartsWith("All", StringComparison.OrdinalIgnoreCase) || //if the greeter is excluded from everything entry.StartsWith("OtherEvent", StringComparison.OrdinalIgnoreCase) || //OR if the greeter is excluded from other events entry.StartsWith("Greet", StringComparison.OrdinalIgnoreCase) //OR if the greeter is excluded from greeting others )) { if (ModEntry.Instance.Monitor.IsVerbose) { ModEntry.Instance.Monitor.Log($"Excluded NPC from greeting someone: {greeterName}", LogLevel.Trace); } return(false); //skip the original method } else if (greeterExclusions.Exists(entry => entry.StartsWith("BeGreeted", StringComparison.OrdinalIgnoreCase))) //if the greeter CAN greet others, but is excluded from being greeted { SkipReply = true; //skip the recipient's reply, if necessary } } if (recipientName != null && exclusions.TryGetValue(recipientName, out List <string> recipientExclusions)) //if the recipient has exclusion data { if (recipientExclusions.Exists(entry => entry.StartsWith("All", StringComparison.OrdinalIgnoreCase) || //if the recipient is excluded from everything entry.StartsWith("OtherEvent", StringComparison.OrdinalIgnoreCase) || //OR if the recipient is excluded from other events entry.StartsWith("BeGreeted", StringComparison.OrdinalIgnoreCase) //OR if the recipient is excluded from being greeted )) { if (ModEntry.Instance.Monitor.IsVerbose) { ModEntry.Instance.Monitor.Log($"Excluded NPC from being greeted: {recipientName}", LogLevel.Trace); } return(false); //skip the original method } else if (recipientExclusions.Exists(entry => entry.StartsWith("Greet", StringComparison.OrdinalIgnoreCase))) //if the recipient CAN be greeted, but is excluded from greeting others { SkipReply = true; //skip the recipient's reply, if necessary } } if (SkipReply) //if the greeting should still happen, but the recipient's reply should be skipped { if (__instance.getHi(recipientName) is string greetingText) //if possible, { __instance.showTextAboveHead(greetingText); //display the greeting (imitating the original code in "NPC.sayHiTo", as of SDV 1.5.4) } if (ModEntry.Instance.Monitor.IsVerbose) { ModEntry.Instance.Monitor.Log($"Excluded NPC from replying to greeting: {recipientName} replying to {greeterName}", LogLevel.Trace); } return(false); //skip the original method } return(true); //run the original method } catch (Exception ex) { ModEntry.Instance.Monitor.LogOnce($"Harmony patch \"{nameof(NPC_sayHiTo)}\" has encountered an error and may revert to default behavior. Full error message:\n{ex.ToString()}", LogLevel.Error); return(true); //run the original method } }