static IEnumerable <WikiElement> Parse(CustomElementArgs args) { WikiPage page = args.Page; lastCat = 0; currentCat = 0; var def = page.Def as ThingDef; str.Clear(); str.AppendLine("<color=cyan><b>Important stats:</b></color>"); ShowStat("Market value", GetStat(def, "MarketValue") * 0.01f, null, '$'); currentCat++; ShowStat("Armor (sharp)", GetStat(def, "StuffPower_Armor_Sharp"), 1f, '%'); ShowStat("Armor (blunt)", GetStat(def, "StuffPower_Armor_Blunt"), 1f, '%'); ShowStat("Armor (heat)", GetStat(def, "StuffPower_Armor_Heat"), 1f, '%'); currentCat++; ShowStat("Melee sharp damage", GetStat(def, "SharpDamageMultiplier"), 1f, '%'); ShowStat("Melee attack cooldown", GetStuffStat(def, "MeleeWeapon_CooldownMultiplier"), 1f, '%', true); ShowStat("Melee blunt damage", GetStat(def, "BluntDamageMultiplier"), 1f, '%'); currentCat++; ShowStat("Max hit points", GetStuffStat(def, "MaxHitPoints"), 1f, '%'); ShowStat("Beauty", GetStuffStat(def, "Beauty"), 1f, '%'); currentCat++; ShowStat("Work to make (items)", GetStuffStat(def, "WorkToMake"), 1f, '%', true); ShowStat("Work to build", GetStuffStat(def, "WorkToBuild"), 1f, '%', true); yield return(WikiElement.Create(str.ToString())); if (def.defName == "RF_Copper" || def.defName == "RF_Tin") { yield break; } yield return(WikiElement.Create("This is a metal alloy that can be created at the Forge:")); yield return(new WikiElement() { DefForIconAndLabel = RFDefOf.RF_Forge }); }
private static string ExtractNoFormattingElements(string text, Dictionary<string, WikiElement> elementSubstitutions) { StringBuilder result = new StringBuilder(); // Explanation of the groups following regex: // It searches for an opening tag, which is either an <element attr="value" (/)> or a <!--comment // (?<!^=+.*) Excludes any element that starts with a repetition of '=' // (?!.*=+$) Excludes any element that ends with a repetition of '=' // Together, these first and last part of the regex are used to exclude elements within a header. // ({0}) Contains the name of the no-formatting elements. // (\\s+[^>]*?|\\s*?) Follows the initial tag with either spaces + content, spaces or nothing. // (/?>) The end of the opening tag, which could be a closing as well. // | OR // (!--) A comment. string elementStart = string.Format(@"(?<!^=+.*)<({0})(\s+[^>]*?|\s*?)(/?>)(?!.*=+$)|<(!--)", ELEMENTS_WITH_NO_FORMATTING); string elementEnd = string.Empty; Regex elementStartRegex = new Regex(elementStart, RegexOptions.IgnoreCase | RegexOptions.Multiline); while (text.Length > 0) { string element = string.Empty; // The name of the element. string attributes = string.Empty; // The attributes of the element (as text). string close = string.Empty; // The closing character(s) of the opening tag. string inside = string.Empty; // The content following the opening tag of the element that is yet to be analyzed. string content = string.Empty; // The content of the element. string tail = string.Empty; // The closing tag of the element. string[] textParts = elementStartRegex.Split(text, 2, 0); // Everything before the non-formatting elements is appended as-is. result.Append(textParts[0]); if (textParts.Length == 5) { // Element + any Attributes. element = textParts[1]; attributes = textParts[2]; close = textParts[3]; inside = textParts[4]; } else if (textParts.Length == 3) { // Comment. element = textParts[1]; inside = textParts[2]; } else { break; } if (close == "/>") { // Empty <tag /> text = inside; } else { // Starting at 1 representing the current element opened. int stack = 1; if (element == "!--") { // Comment. elementStart = "<!--"; elementEnd = "-->"; // Setting the stack to 0 will skip the search loop for a closing tag of same depth. // The first closing tag found will be taken to close the element. stack = 0; } else { elementStart = string.Format(@"<{0}(\s+[^/]*?)?>", element); elementEnd = string.Format("<\\/{0}\\s*>", element); } Regex openingsRegex = new Regex(elementStart, RegexOptions.IgnoreCase); Regex closingsRegex = new Regex(elementEnd, RegexOptions.IgnoreCase); MatchCollection openings = openingsRegex.Matches(inside, 0); MatchCollection closings = closingsRegex.Matches(inside, 0); int i = 0, j = 0; // This loop looks for the j index of a matching closing tag for the current element. // Any new opening tag will increase the stack and require an additional closing tag. while (stack > 0) { if (j == closings.Count) { // No more closing tags => needless to keep counting. break; } if (i < openings.Count && openings[i].Index < closings[j].Index) { // We found another opening before a closing. Increase the stack. stack++; i++; } else { // We found a closing element. Decrease the stack. if (--stack == 0) break; // Bingo. else j++; } } // At this point, we know we can substring from 0 to the index of the closing tag OR to the end. if (j == closings.Count) { // No end tag - let it run out to the end of the text. content = inside.Substring(0, inside.Length); tail = string.Empty; text = string.Empty; } else { content = inside.Substring(0, closings[j].Index); tail = closings[j].Value; text = inside.Substring(closings[j].Index + closings[j].Length); } } // Substitute the occurences of the non-formatting elements and their content by a GUID. // The substitution can later be reversed to retrieve the content untouched. string elementGUID = Tools.GetRandomGUID(); if (element == "!--") result.Append("<!-- " + elementGUID + " -->"); // The original tags are used in subsequent methods below. else if (element == "pre" || element == "code" || element == "sql") result.AppendFormat("<{0}{1}>{2}</{0}>", element, attributes, elementGUID); // The original tags are used in subsequent methods below. else result.Append(elementGUID); WikiElement substitutedElement = new WikiElement(element, content, WikiAttributes.ParseAttributes(attributes)); elementSubstitutions.Add(elementGUID, substitutedElement); } return result.ToString(); }
internal void SetParent(WikiElement parent) { _parent = parent; }