public static string FindConflictingConditions(string conds, char seperator = ',', int limit = 5, bool strict = true) { string[] conditions = conds.Split(seperator); // Force the limit to be 4~16 // Less then 4 would not allow enough freedom // While more then 16 would take a lot of time to process limit = Math.Max(4, Math.Min(16, limit)); if (conditions.Length > limit) { return("The conditions list contains more then the maximum `" + limit + "` conditions allowed"); } List <string> cache = new List <string>(); string weekdayFound = null; string seasonFound = null; string marriageFound = null; string engagementFound = null; int negatedSeasons = 0; int negatedDays = 0; string positiveYearMod = null; int positiveYearStart = 0; int negativeYearStart = 0; string houseLevel = null; string weatherCondition = null; foreach (string condition in conditions) { /* * There are times where a condition becomes deprecated due to a change in the condition system * Deprecated conditions are only reported as being deprecated if strict conflict detection is used * Otherwise, the deprecated conditions are treated as generic conditions * * Note that deprecated conditions are internally replaced by their newer equivalent when resolving * This means that deprecated conditions not only do not have full conflict resultion, they also are one step slower to process * * Further, as said earlier, deprecated conditions are treated as generic conditions rather then connected conditions * What this means, is that where `year>1,year>2` would be reported as having conflicts, the same is not true for `secondYear,thirdYear` * This is because of the difference between generic and connected conditions * Generic conditions are true/false flags that do not have a connection to any other condition * Connected conditions are true/false flags that do have this connected behaviour * * Because of this, extra resolution logic exists in order to detect and report as many of the possible conflicts as it can * Deprecated conditions do not trigger this resolution logic, and have in fact been purposefully excluded from it * * */ if (strict) { if (DeprecatedConditions.ContainsKey(condition)) { return(Format(ConditionDeprecated, new string[] { condition, DeprecatedConditions[condition] })); } else if (DeprecatedConditions.ContainsKey('!' + condition)) { return(Format(ConditionDeprecated, new string[] { '!' + condition, '!' + DeprecatedConditions[condition] })); } } // Check for conditions where another condition would be faster to resolve the true/false state but the end-result is the same if (strict && ConditionRecommend.ContainsKey(condition)) { return("The `" + condition + "` condition should be replaced by the functionally equivalent `" + ConditionRecommend[condition] + "` condition as it is faster"); } // Check for conditions that are at least double-negatives if (condition.StartsWith("!!")) { return("The `" + condition + "` condition performs negation more then once"); } // Check if a condition is in the list more then once if (strict && cache.Contains(condition)) { return("The `" + condition + "` condition has been specified more then once"); } // Check if the positive to this condition is in the list, since that would result in a always-false situation else if (condition.StartsWith("!") && cache.Contains(condition.Substring(1))) { return(Format(ConditionAndNegation, new string[] { condition.Substring(1), condition })); } // Check if the negative to this condition is in the list, since that would result in a always-false situation else if (cache.Contains('!' + condition)) { return(Format(ConditionAndNegation, new string[] { condition, '!' + condition })); } // Check for weather conditions if (condition.StartsWith("weather")) { if (weatherCondition != null) { return(Format(ConditionExclusive, new string[] { condition, weatherCondition })); } else { weatherCondition = condition; } string t = CheckSnow(cache); if (t != null) { return(t); } } // Check for house upgrade based conditions where conflicts may occur if (condition.StartsWith("houseLevel=")) { if (houseLevel != null) { return(Format(ConditionExclusive, new string[] { condition, houseLevel })); } else { houseLevel = condition; } if (Convert.ToInt32(condition.Substring(11)) < 0) { return(Format(ConditionFormat, new string[] { condition })); } } // Check for the positive year-based conditions where conflicts may occur if (condition.StartsWith("year")) { if (positiveYearMod != null) { return(Format(ConditionExclusive, new string[] { condition, positiveYearMod })); } positiveYearMod = condition; if (condition.StartsWith("year>")) { try { int posYear = Convert.ToInt32(condition.Substring(5)); if (posYear < 1) { return(Format(ConditionFormat, new string[] { condition })); } if (negativeYearStart < posYear) { return(Format(ConditionExclusive, new string[] { condition, "!year>" + negativeYearStart })); } positiveYearStart = posYear; } catch { return(Format(ConditionFormat, new string[] { condition })); } } } // Check for the negative year-based conditions where conflicts may occur else if (condition.StartsWith("!year>")) { try { if (negativeYearStart > 0) { return(Format(ConditionExclusive, new string[] { condition, "!year>" + negativeYearStart })); } int negYear = Convert.ToInt32(condition.Substring(5)); if (negYear < 1) { return(Format(ConditionFormat, new string[] { condition })); } if (positiveYearStart >= negYear) { return(Format(ConditionExclusive, new string[] { condition, "year>" + positiveYearStart })); } negativeYearStart = negYear; } catch { return(Format(ConditionFormat, new string[] { condition })); } } // Check for situations where marriage conditions may conflict else if (condition.StartsWith("married")) { if (engagementFound != null) { return(Format(ConditionExclusive, new string[] { condition, marriageFound })); } else if (strict && condition == "married" && marriageFound != null) { return(Format(ConditionImplied, new string[] { condition, marriageFound })); } else if (strict && cache.Contains("married")) { return(Format(ConditionImplied, new string[] { "married", condition })); } else if (marriageFound != null) { return(Format(ConditionExclusive, new string[] { condition, marriageFound })); } else { marriageFound = condition; } } // Check for situations where engagement conditions may conflict else if (condition.StartsWith("engaged")) { if (engagementFound != null) { return(Format(ConditionExclusive, new string[] { condition, engagementFound })); } else if (strict && condition == "engaged" && engagementFound != null) { return(Format(ConditionImplied, new string[] { condition, engagementFound })); } else if (strict && cache.Contains("engaged")) { return(Format(ConditionImplied, new string[] { "engaged", condition })); } else if (marriageFound != null) { return(Format(ConditionExclusive, new string[] { condition, engagementFound })); } else { engagementFound = condition; } } // Check for multiple weekday or season conditions where neither have been negated, since it cannot be multiple days or seasons at the same time // Also check if any negated weekday or season conditions apply, since having both a fixed day/season and a not day/season is pointless else { switch (condition) { case "sunday": case "monday": case "tuesday": case "wednesday": case "thursday": case "friday": case "saturday": if (weekdayFound != null) { return(Format(ConditionExclusive, new string[] { condition, weekdayFound })); } else { weekdayFound = condition; } if (!strict) { break; } string[] matchedDays = (string[])cache.Intersect(WeekdaysInverted); if (matchedDays.Any()) { return(Format(ConditionImplied, new string[] { condition, matchedDays[0] })); } break; case "spring": case "summer": case "fall": case "winter": if (seasonFound != null) { return(Format(ConditionExclusive, new string[] { condition, seasonFound })); } else { seasonFound = condition; } string t = CheckSnow(cache); if (t != null) { return(t); } if (!strict) { break; } string[] matchedSeasons = (string[])cache.Intersect(SeasonsInverted); if (matchedSeasons.Any()) { return(Format(ConditionImplied, new string[] { condition, matchedSeasons[0] })); } break; case "!spring": case "!summer": case "!fall": case "!winter": negatedSeasons++; if (strict && negatedSeasons > 2) { return("The condition list contains more then 2 negative season conditions, use a single positive season condition instead"); } string ts = CheckSnow(cache); if (ts != null) { return(ts); } break; case "!sunday": case "!monday": case "!tuesday": case "!wednesday": case "!thursday": case "!friday": case "!saturday": if (!strict) { break; } negatedDays++; if (negatedDays > 5) { return("The condition list contains more then 5 negative weekday conditions, use a single positive weekday condition instead"); } if (negatedDays > 4 && !cache.Contains("!sunday") && !cache.Contains("!saturday")) { return("The condition list contains negated weekday conditions for all days except saturday and sunday, use the positive `weekend` condition instead"); } break; } } // If this condition has passed all the checks, we cache the condition so other conditions in the list can refer to it cache.Add(condition); } return(null); }
public static bool CheckCondition(string condition, Func <string, bool?> customResolver = null) { // Check if this is a negated condition if (condition.StartsWith("!")) { // Check if it is negated multiple times, as this risks a infinite loop situation if (condition.StartsWith("!!")) { // If we find multiple negations, we forcibly return false return(false); } else { // If we dont find multiple negations, we negate the result of a positive condition lookup return(!CheckCondition(condition.Substring(1))); } } // If it is not a negated condition, we perform a positive condition lookup else { // First, we check for the mostly-static conditions switch (condition) { // Conditions for the possible weather states case "weatherSun": case "weatherRain": case "weatherSnow": case "weatherStorm": case "weatherFestival": case "weatherWedding": case "weatherFallDebris": case "weatherSummerDebris": return(condition == WeatherTypes[Game1.weatherIcon]); // Special condition for "Any debris weather" case "weatherDebris": return(Game1.weatherIcon == 3 || Game1.weatherIcon == 6); // Special condition for "Weather only possible in the current season" case "weatherSeasonal": switch (Game1.currentSeason) { case "summer": return(Game1.weatherIcon == 3); case "fall": return(Game1.weatherIcon == 6); case "winter": return(Game1.weatherIcon == 7); default: return(false); } // Condition for if one of the two special weather states is the current state case "weatherSpecial": return(Game1.weatherIcon < 2); // Conditions for each of the 7 days case "sunday": case "monday": case "tuesday": case "wednesday": case "thursday": case "friday": case "saturday": return(condition == Weekdays[Game1.dayOfMonth % 7]); // Special condition for either saturday or sunday case "weekend": int day = Game1.dayOfMonth % 7; return(day == 0 || day == 6); // Conditions for each of the 4 seasons case "spring": case "summer": case "fall": case "winter": return(condition == Game1.currentSeason); // Generic condition that is true whenever the player is married, irrelevant of the person they are married to case "married": return(Game1.player.spouse != null && !Game1.player.spouse.Contains("engaged")); // Generic condition that is true whenever the player is engaged to be married, irrelevant of the person they are engaged to case "engaged": return(Game1.player.spouse != null && Game1.player.spouse.Contains("engaged")); // Condition that is only true after the earthquake event has happened case "earthquake": return(Game1.stats.daysPlayed > 31); // Condition that is only true after the player has received the rusty key case "rustyKey": return(Game1.player.hasRustyKey); // Condition that is only true after the player has received the skull key case "skullKey": return(Game1.player.hasSkullKey); // Condition that is only true after the player has received the club card case "clubMember": return(Game1.player.hasClubCard); // Condition that is only true after the player has learned to speak the dwarven language case "dwarfSpeak": return(Game1.player.canUnderstandDwarves); // New and updated condition handling for more complex conditions default: // Handle marriage for any NPC instead of just the default bachelors (Backwards compatible) if (condition.StartsWith("married")) { return(Game1.player.spouse != null && Game1.player.spouse.Equals(condition.Substring(7))); } // Handle engagement for any NPC instead of just the default bachelors *new* if (condition.StartsWith("engaged")) { return(Game1.player.spouse != null && Game1.player.spouse.Contains(condition.Substring(7)) && Game1.player.spouse.Contains("engaged")); } // Allow year-based conditions for during any specific year if (condition.StartsWith("year=")) { return(Game1.year == Convert.ToInt32(condition.Substring(5))); } // Allow year-based conditions for after any specific year if (condition.StartsWith("year>")) { return(Game1.year > Convert.ToInt32(condition.Substring(5))); } // House upgrade level conditions if (condition.StartsWith("houseLevel=")) { return(Game1.player.houseUpgradeLevel == Convert.ToInt32(condition.Substring(11))); } // Farm type conditions if (condition.StartsWith("farmType=")) { return(Game1.whichFarm == Convert.ToInt32(condition.Substring(9))); } // Time conditions *new* if (condition.StartsWith("time>")) { return(Game1.timeOfDay > Convert.ToInt32(condition.Substring(5))); } // Check if the condition is a deprecated one, and if so, evaluate its replacement instead if (DeprecatedConditions.ContainsKey(condition)) { return(CheckCondition(DeprecatedConditions[condition])); } if (customResolver != null) { bool?x = customResolver(condition); if (x != null) { return((bool)x); } } // Check for mail flags return(Game1.player.mailReceived.Contains(condition)); } } }