/*********************************************************************************************** * ToHtml / 2014-08-01 / Wethospu * * * * Converts this effect object to html representration. * * * * Returns representation. * * path: Name of current path. Needed for enemy links. * * enemies: List of enemies in the path. Needed for enemy links. * * indent: Base indent for the HTML. * * * ***********************************************************************************************/ public string ToHtml(string path, double coefficient, string weapon, List<Enemy> enemies, Enemy baseEnemy, int indent) { var htmlBuilder = new StringBuilder(); _type = LinkGenerator.CreateEnemyLinks(HandleEffect(_type, 0, weapon, 1, 0, 0, baseEnemy), path, enemies); // Replace end dot with double dot if the effect has sub effects (visually looks better). / 2015-10-01 / Wethospu if (_type[_type.Length - 1] == '.' && SubEffects.Count > 0) _type = _type.Substring(0, _type.Length - 1) + ':'; htmlBuilder.Append(Gw2Helper.AddTab(indent)).Append("<p>").Append(_type).Append("</p>").Append(Constants.LineEnding); htmlBuilder.Append(Gw2Helper.AddTab(indent)).Append("<ul>").Append(Constants.LineEnding); foreach (var subEffect in SubEffects) { htmlBuilder.Append(Gw2Helper.AddTab(indent + 1)).Append("<li>"); htmlBuilder.Append(LinkGenerator.CreateEnemyLinks(HandleEffect(subEffect, coefficient, weapon, HitCount, HitLength, HitFrequency, baseEnemy), path, enemies)); htmlBuilder.Append("</li>").Append(Constants.LineEnding); } htmlBuilder.Append(Gw2Helper.AddTab(indent)).Append("</ul>").Append(Constants.LineEnding); return htmlBuilder.ToString(); }
/// <summary> /// Generates the HTML representation. /// </summary> public string ToHtml(Attack owner, Enemy attackOwner, int baseIndent) { var htmlBuilder = new StringBuilder(); Type = EffectHandler.HandleEffect(Type, this, owner, attackOwner); // Replace end dot with a double dot if the effect has sub effects (visually looks better). if (Type[Type.Length - 1] == '.' && SubEffects.Count > 0) Type = Type.Substring(0, Type.Length - 1) + ':'; htmlBuilder.Append(Gw2Helper.AddTab(baseIndent)).Append("<p>").Append(Helper.ToUpper(Type)).Append("</p>").Append(Constants.LineEnding); htmlBuilder.Append(Gw2Helper.AddTab(baseIndent)).Append("<ul>").Append(Constants.LineEnding); foreach (var subEffect in SubEffects) { htmlBuilder.Append(Gw2Helper.AddTab(baseIndent + 1)).Append("<li>"); var str = EffectHandler.HandleEffect(subEffect, this, owner, attackOwner); htmlBuilder.Append(Helper.ToUpper(str.Replace("\\:", ":"))); htmlBuilder.Append("</li>").Append(Constants.LineEnding); } htmlBuilder.Append(Gw2Helper.AddTab(baseIndent)).Append("</ul>").Append(Constants.LineEnding); return htmlBuilder.ToString(); }
private static SubEffectInformation ExtractInformation(SubEffectInformation effect, SubEffect subEffect, TagData text, Attack baseAttack, Enemy baseEnemy) { var effectType = subEffect.Name; var effectData = text.Data.Split(':'); effect.name = text.Tag.ToLower(); effect.amount = -1; effect.duration = 0.0; effect.stacks = 1; effect.buff = ""; effect.icon = effect.name; effect.suffix = "damage"; if (effectType == EffectType.Damage || effectType == EffectType.DamageFixed || effectType == EffectType.DamagePercent) { if (effectData[0].Equals("-")) effect.amount = baseAttack.Coefficient; else if (effectData[0].Equals("?")) effect.amount = -1; else effect.amount = Helper.ParseD(effectData[0]); effect.icon = "damage"; effect.stacks = 1; } else if (effectType == EffectType.Healing || effectType == EffectType.HealingPercent) { if (effectData[0].Equals("-")) effect.amount = baseAttack.Coefficient; else if (effectData[0].Equals("?")) effect.amount = -1; else effect.amount = Helper.ParseD(effectData[0]); effect.suffix = "healing"; effect.icon = "healing"; effect.stacks = 1; } if (effectType == EffectType.Boon || effectType == EffectType.Condition || effectType == EffectType.Control) { baseEnemy.Tags.Add(effect.name); } if (effectType == EffectType.Agony || effectType == EffectType.Boon || effectType == EffectType.Condition) { if (effectData[0].Equals("-")) effect.duration = baseAttack.Coefficient; else if (effectData[0].Equals("?")) effect.duration = -1; else effect.duration = Helper.ParseD(effectData[0]); if (effectType == EffectType.Agony || effect.name.Equals("bleeding") || effect.name.Equals("torment") || effect.name.Equals("burning") || effect.name.Equals("poison") || effect.name.Equals("confusion") || effect.name.Equals("regeneration") || effect.name.Equals("might")) effect.amount = effect.duration; if (effectData.Length > 1) effect.stacks = Helper.ParseI(effectData[1]); if (subEffect.StacksDuration || effectType == EffectType.Boon) effect.suffix = "seconds"; if (effect.name.Equals("regeneration")) effect.suffix = "healing"; if (effect.name.Equals("retaliation")) effect.suffix = "damage per hit"; if (effect.name.Equals("might")) effect.suffix = "more damage"; if (effect.name.Equals("retaliation") || effect.name.Equals("might")) effect.amount = 1; } if (effectType == EffectType.Control) { if (effectData[0].Equals("?")) effect.duration = 0; else effect.duration = Helper.ParseD(effectData[0]); if (effectData.Length > 1) effect.stacks = Helper.ParseI(effectData[1]); } if (effectType == EffectType.Buff) { effect.buff = effectData[0]; effect.icon = effect.buff; if (effectData.Length > 1 && effectData[1].Length > 0) effect.duration = Helper.ParseD(effectData[1]); if (effectData.Length > 2 && effectData[2].Length > 0) effect.stacks = Helper.ParseI(effectData[2]); if (effectData.Length > 3 && effectData[3].Length > 0) effect.icon = effectData[3]; if (effectData.Length > 4) subEffect.StacksDuration = Helper.ParseI(effectData[4]) > 0 ? true : false; } return effect; }
/*********************************************************************************************** * AttackToHTML / 2015-09-15 / Wethospu * * * * Converts this attack object to html representration. * * * * Returns representation. * * path: Name of current path. Needed for enemy links. * * enemies: List of enemies in the path. Needed for enemy links. * * indent: Base indent for the HTML. * * * ***********************************************************************************************/ public string AttackToHTML(string path, List<Enemy> enemies, Enemy baseEnemy, int indent) { if (_name.Equals("")) Helper.ShowWarningMessage("Enemy " + baseEnemy.Name + " has no attack name."); var htmlBuilder = new StringBuilder(); // Add attack name. htmlBuilder.Append(Gw2Helper.AddTab(indent + 1)).Append("<p class=\"enemy-attack\"><span class=\"enemy-attack-name\">").Append(Helper.ConvertSpecial(Helper.ToUpperAll(LinkGenerator.CreateEnemyLinks(_name, path, enemies)))); htmlBuilder.Append("</span>").Append(" "); // Add other data. if (!Animation.Equals("")) htmlBuilder.Append("<span class=\"animation-unit\"><i>").Append(Helper.ConvertSpecial(Helper.ToUpper(LinkGenerator.CreateEnemyLinks(Animation, path, enemies)))).Append("</i>. </span>"); if (Cooldown > -1 || _internalCooldown > -1) { htmlBuilder.Append("<span class=\"cooldown-unit\" title=\"Skill cooldown\"><span class=\"cooldown\""); if (Cooldown > -1) htmlBuilder.Append(" data-amount=\"").Append(Cooldown).Append("\""); if (_internalCooldown > -1) htmlBuilder.Append(" data-internal=\"").Append(_internalCooldown / 1000).Append("\""); htmlBuilder.Append("></span>").Append(Constants.Space).Append("<span class=").Append(Constants.IconClass).Append(" data-src=\"cooldown\">Cooldown</span> </span>"); } if (_minimumRange > -1 || _maximumRange > -1) { htmlBuilder.Append("<span class=\"range-unit\" title=\"Activation range\">"); htmlBuilder.Append(_minimumRange > -1 ? "" + _minimumRange : "?").Append("-").Append(_maximumRange > -1 ? "" + _maximumRange : "?"); htmlBuilder.Append(Constants.Space).Append("<span class=").Append(Constants.IconClass).Append(" data-src=\"range\">Range</span> </span>"); } htmlBuilder.Append("</p>").Append(Constants.LineEnding); htmlBuilder.Append(Gw2Helper.AddTab(indent + 1)).Append("<div class=\"enemy-attack-effect\">").Append(Constants.LineEnding); // Add attack effects. foreach (var effect in Effects) htmlBuilder.Append(effect.ToHtml(path, _coefficient, _weapon, enemies, baseEnemy, indent + 2)); htmlBuilder.Append(Gw2Helper.AddTab(indent + 1)).Append("</div>").Append(Constants.LineEnding); return htmlBuilder.ToString(); }
private static void VerifyEnemy(Enemy currentEnemy) { if (currentEnemy == null) return; if (currentEnemy.Paths.Count == 0) ErrorHandler.ShowWarning("Path not set for previous enemy " + currentEnemy.Name + "."); if (currentEnemy.Rank.Length == 0) ErrorHandler.ShowWarning("Rank not set for previous enemy " + currentEnemy.Name + "."); }
/// <summary> /// Adds alternative names to a given enemy. These are used for the site search and enemy linking. /// </summary> private static void HandleAlternativeNames(string data, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"alt='alt1'|'alt2'|'altN'\"!"); if (data.Contains('_')) ErrorHandler.ShowWarning("Alt names " + data + " containts '_'. Replace them with ' '!"); var altNames = data.Split('|'); currentEnemy.AltNames.Clear(); foreach (var altName in altNames) currentEnemy.AddAlternativeName(altName); }
/*********************************************************************************************** * EnemyLoop / 2014-08-01 / Wethospu * * * * Main process loop for enemies. * * * * tag: Tag of the line. * * data: Data of the line. * * enemies: Output. List of processed enemies. * * * ***********************************************************************************************/ private static int EnemyLoop(string tag, string data, List<Enemy> enemies, Dictionary<string, EnemyAttributes> enemyAttributes) { if (!tag.Equals("name") && !tag.Equals("id") && _currentEnemy != null & _currentEnemy.IsNameCopied) Helper.ShowWarning("ID or name not explicitly set for a copied enemy."); if (tag.Equals("copy")) { if (_currentEnemy != null) enemies.Add(_currentEnemy); var found = FindEnemy(data, enemies); if (found != null) { _currentEnemy = Helper.CloneJson(found); _currentEnemy.IsNameCopied = true; _currentEnemy.AreAnimationsCopied = true; } else Helper.ShowWarning("Copying failed. Enemy not found!"); } else if (tag.Equals("name")) { if (data.Length > 0) { if (_currentEnemy != null && !_currentEnemy.IsNameCopied) { enemies.Add(_currentEnemy); if (_currentEnemy.Paths.Count == 0) Helper.ShowWarning("Path not set for enemy " + _currentEnemy.Name); } if (data.Contains('_')) Helper.ShowWarning("Enemy name " + data + " containts '_'. Replace them with ' '!"); // For copies only set the name. / 2015-10-05 / Wethospu if (_currentEnemy != null && _currentEnemy.IsNameCopied) _currentEnemy.Name = data; else _currentEnemy = new Enemy(data); _currentEnemy.IsNameCopied = false; _currentAttack = null; _currentEffect = null; } else Helper.ShowWarning("Missing info. Use \"name='name'\"!"); } else if (tag.Equals("id")) { if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else if (data.Length > 0) { _currentEnemy.IsNameCopied = false; var ids = data.Split('|'); // Enemies can have multiple genders if there are model variations. / 2015 - 09 - 28 / Wethospu // Each model has a different id so store old ones to get all added. / 2015-09-28 / Wethospu var oldGenders = ""; foreach (var id in ids) { _currentEnemy.InternalIds.Add(Helper.ParseI(id)); if (enemyAttributes.ContainsKey(id)) { _currentEnemy.Attributes = enemyAttributes[id]; if (oldGenders.Length > 0) { var genders = oldGenders.Split('|'); // If the sex is already there it can be ignored. / 2015-09-28 / Wethospu if (genders.Contains(_currentEnemy.Attributes.Gender)) _currentEnemy.Attributes.Gender = oldGenders; else _currentEnemy.Attributes.Gender = oldGenders + "|" + _currentEnemy.Attributes.Gender; } _currentEnemy.Rank = _currentEnemy.Attributes.GetRank(); oldGenders = _currentEnemy.Attributes.Gender; } else Helper.ShowWarning("Id " + data + " not found in enemy attributes."); } } else Helper.ShowWarning("Missing info. Use \"id='id'\"!"); } else if (tag.Equals("path")) { if (data.Length == 0) Helper.ShowWarning("Missing info. Use \"path='path1'|'path2'|'pathN'\"!"); if (data.Contains(" ")) { Helper.ShowWarning("' ' found. Use syntax \"path='path1'|'path2'|'pathN'\""); data = data.Replace(' ', '|'); } if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else _currentEnemy.Paths = new List<string>(data.ToLower().Split('|')); } else if (tag.Equals("rank")) { if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else if (data.Length > 0) { _currentEnemy.Rank = data.ToLower(); if (!LinkGenerator.EnemyCategories.Contains(_currentEnemy.Rank)) Helper.ShowWarning("Rank " + _currentEnemy.Rank + " not recognized. Check syntax for correct categories."); } else Helper.ShowWarning("Missing info. Use \"rank='rank'\"!"); } else if (tag.Equals("alt")) { if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else if (data.Length > 0) { if (data.Contains('_')) Helper.ShowWarning("Alt names " + data + " containts '_'. Replace them with ' '!"); var altNames = data.Split('|'); _currentEnemy.AltNames.Clear(); foreach (var altName in altNames) _currentEnemy.AddAlt(altName); } else Helper.ShowWarning("Missing info. Use \"alt='alt1'|'alt2'|'altN'\"!"); } else if (tag.Equals("image")) { if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else if (data.Length > 0) { if (_currentEnemy.AreAnimationsCopied) { _currentEnemy.Medias.Clear(); _currentEnemy.AreAnimationsCopied = false; } _currentEnemy.Medias.Add(new Media(data)); } else Helper.ShowWarning("Missing info. Use \"image='imagelink'\"!"); } else if (tag.Equals("level")) { if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else if (data.Length > 0) { _currentEnemy.Level = Helper.ParseI(data); } else Helper.ShowWarning("Missing info. Use \"level='amount'\""); } else if (tag.Equals("scaling")) { if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else if (data.Length > 0) { var scalingSplit = data.Split('|'); _currentEnemy.ScalingType = scalingSplit[0]; if (scalingSplit.Length > 1) { int result; if (int.TryParse(scalingSplit[1], out result)) _currentEnemy.ScalingFractal = result; else Helper.ShowWarning("Fractal scale " + scalingSplit[1] + " is not an integer!"); if (scalingSplit.Length > 2) { if (int.TryParse(scalingSplit[2], out result)) _currentEnemy.ScalingLevel = result; else Helper.ShowWarning("Enemy level " + scalingSplit[2] + " is not an integer!"); } } } else Helper.ShowWarning("Missing info. Use \"scaling='type'|'fractal scale'|'enemy level'\"!"); } else if (tag.Equals("attack")) { if (_currentEnemy == null) Helper.ShowWarning("Enemy not initialized with name."); else if (_currentEnemy.Rank.Length == 0) Helper.ShowWarningMessage("Rank not set for enemy " + _currentEnemy.Name + ". Please fix!"); return 1; } else if (tag.Equals("tactic")) { // Set validity to over max so custom tactics never get overridden. / 2015-08-09 / Wethospu _currentEnemy.TacticValidity = 2.0; if (data.Length > 0) _currentEnemy.Tactics.AddTactics(data); else Helper.ShowWarning("Missing info. Use \"tactic='tactic1'|'tactic2'|'tacticN'\"."); } else if (tag.Equals("health")) { if (data.Length > 0) { _currentEnemy.Attributes.Multipliers.HealthMultiplier = Helper.ParseD(data); // If vitality is not set, initialize it with something sensible so the page can calculate something. / 2015-09-10 / Wethospu if (_currentEnemy.Attributes.Multipliers.Vitality < 0.1) _currentEnemy.Attributes.Multipliers.Vitality = 1; if (Helper.ParseD(data) > 1000) Helper.ShowWarning("Health values should be multipliers. Calculate the multiplier."); } else Helper.ShowWarning("Missing info. Use \"health='amount'."); } else if (tag.Equals("toughness")) { if (data.Length > 0) { _currentEnemy.Attributes.Multipliers.Toughness = Helper.ParseD(data); if (Helper.ParseD(data) > 100) Helper.ShowWarning("Toughness values should be multipliers. Calculate the multiplier."); } else Helper.ShowWarning("Missing info. Use \"toughness='amount'."); } else if (tag.Equals("armor")) { Helper.ShowWarning("Armor values shouldn't be used. Calculate the toughness multiplier."); } else if (tag.Equals("condition")) { if (data.Length > 0) { _currentEnemy.Attributes.Multipliers.ConditionDamage = Helper.ParseD(data); if (Helper.ParseD(data) > 100) Helper.ShowWarning("Condition damage values should be multipliers. Calculate the multiplier."); } else Helper.ShowWarning("Missing info. Use \"condition='amount'."); } else if (tag.Equals("race")) { if (data.Length > 0) { _currentEnemy.Attributes.Family.Name = data; } else Helper.ShowWarning("Missing info. Use \"race='value'."); } else if (tag.Equals("tag")) { if (data.Length > 0) { var split = data.Split('|'); foreach (var str in split) _currentEnemy.Tags.Add(str.ToLower()); } else Helper.ShowWarning("Missing info. Use \"tag='tactic1'|'tactic2'|'tacticN'\"!"); } // Normal content. else if (tag.Equals("")) { // Preprocess the line to avoid doing same stuff 25+ times. _currentEnemy.Tactics.AddLine(LinkGenerator.CreatePageLinks(LinkGenerator.CheckLinkSyntax(data))); } else if (tag.Equals("type") || tag.Equals("effect") || tag.Equals("cooldown") || tag.Equals("additional") || tag.Equals("animation")) Helper.ShowWarning("Missing attack name (\"attack='name'\")!"); else Helper.ShowWarning("Unrecognized tag: " + tag); return 0; }
/// <summary> /// Generates the HTML representation. /// </summary> public string ToHTML(Enemy attackOwner, int baseIndent) { if (Name.Equals("")) ErrorHandler.ShowWarningMessage("Enemy " + attackOwner.Name + " has no attack name."); var htmlBuilder = new StringBuilder(); // Add attack name. htmlBuilder.Append(Gw2Helper.AddTab(baseIndent + 1)).Append("<p class=\"enemy-attack\"><span class=\"enemy-attack-name\">").Append(Helper.ConvertSpecial(Helper.ToUpperAll(Name))); htmlBuilder.Append("</span>").Append(" "); // Add other data. if (!Animation.Equals("")) htmlBuilder.Append("<span class=\"animation-unit\"><i>").Append(Helper.ConvertSpecial(Helper.ToUpper(Animation))).Append("</i>. </span>"); if (Cooldown > -1 || InternalCooldown > -1) { htmlBuilder.Append("<span class=\"cooldown-unit\" title=\"Skill cooldown\"><span class=\"cooldown\""); if (Cooldown > -1) htmlBuilder.Append(" data-amount=\"").Append(Cooldown).Append("\""); if (InternalCooldown > -1) htmlBuilder.Append(" data-internal=\"").Append(InternalCooldown / 1000).Append("\""); htmlBuilder.Append("></span>").Append(Constants.Space).Append("<span class=").Append(Constants.IconClass).Append(" data-src=\"cooldown\">Cooldown</span> </span>"); } if (MinimumRange > -1 || MaximumRange > -1) { htmlBuilder.Append("<span class=\"range-unit\" title=\"Activation range\">"); htmlBuilder.Append(MinimumRange > -1 ? "" + MinimumRange : "?").Append("-").Append(MaximumRange > -1 ? "" + MaximumRange : "?"); htmlBuilder.Append(Constants.Space).Append("<span class=").Append(Constants.IconClass).Append(" data-src=\"range\">Range</span> </span>"); } htmlBuilder.Append("</p>").Append(Constants.LineEnding); htmlBuilder.Append(Gw2Helper.AddTab(baseIndent + 1)).Append("<div class=\"enemy-attack-effect\">").Append(Constants.LineEnding); // Add effects. foreach (var effect in Effects) htmlBuilder.Append(effect.ToHtml(this, attackOwner, baseIndent + 2)); htmlBuilder.Append(Gw2Helper.AddTab(baseIndent + 1)).Append("</div>").Append(Constants.LineEnding); return htmlBuilder.ToString(); }
/// <summary> /// Adds a media file to a given enemy. /// </summary> private static void HandleMedia(string data, Enemy currentEnemy) { if (currentEnemy.AreAnimationsCopied) { currentEnemy.Medias.Clear(); currentEnemy.AreAnimationsCopied = false; } currentEnemy.HandleMedia(data, Constants.EnemyMediaFolder); }
/// <summary> /// Sets a custom level for a given enemy. By default, this is calculated from the path base level. /// </summary> private static void HandleLevel(string data, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"level='amount'\""); currentEnemy.Level = Helper.ParseI(data); }
/// <summary> /// Sets datamined id to a given enemy. Automatically loads datamined data. /// </summary> private static void HandleId(string data, Enemy currentEnemy, Dictionary<string, EnemyAttributes> enemyAttributes) { if (data.Length == 0) { ErrorHandler.ShowWarning("Missing info. Use \"id='id'\"!"); return; } currentEnemy.IsNameCopied = false; var ids = data.Split('|'); // Enemies can have multiple genders if there are model variations. // Each model has a different id so store old ones to get all added. var oldGenders = ""; foreach (var id in ids) { currentEnemy.InternalIds.Add(Helper.ParseI(id)); if (enemyAttributes.ContainsKey(id)) { // Different enemies may share attributes (for exampled allied enemies). currentEnemy.Attributes = enemyAttributes[id]; if (oldGenders.Length > 0) { var genders = oldGenders.Split('|'); if (genders.Contains(currentEnemy.Attributes.Gender)) currentEnemy.Attributes.Gender = oldGenders; else currentEnemy.Attributes.Gender = oldGenders + "|" + currentEnemy.Attributes.Gender; } } else ErrorHandler.ShowWarning("Id " + data + " not found in enemy attributes."); } }
/// <summary> /// Most health values are acquired automatically from datamined data. This is used for special cases. /// </summary> private static void HandleHealth(string data, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"health='amount'."); currentEnemy.Attributes.Multipliers.HealthMultiplier = Helper.ParseD(data); // If vitality is not set, initialize it with something sensible so the page can calculate something. if (currentEnemy.Attributes.Multipliers.Vitality < 0.0001) currentEnemy.Attributes.Multipliers.Vitality = 1; if (currentEnemy.Attributes.Multipliers.HealthMultiplier > 1000) ErrorHandler.ShowWarning("Health values should be multipliers. Calculate the multiplier."); }
/// <summary> /// Copies an enemy to a given enemy. /// </summary> private static Enemy HandleCopy(Enemy foundEnemy) { if (foundEnemy == null) { ErrorHandler.ShowWarning("Copying failed. Enemy not found!"); return null; } var currentEnemy = Helper.CloneJson(foundEnemy); currentEnemy.IsNameCopied = true; currentEnemy.AreAnimationsCopied = true; return currentEnemy; }
/// <summary> /// Most condition damage values are acquired automatically from datamined data. This is used for special cases. /// </summary> private static void HandleCondition(string data, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"condition='amount'."); currentEnemy.Attributes.Multipliers.ConditionDamage = Helper.ParseD(data); if (currentEnemy.Attributes.Multipliers.ConditionDamage > 100) ErrorHandler.ShowWarning("Condition damage values should be multipliers. Calculate the multiplier."); }
private static void HandleAttackId(string data, Attack currentAttack, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"id=number\"."); currentAttack.LoadAttributes(Helper.ParseI(data), currentEnemy.Attributes); }
/// <summary> /// Converts raw effect data to html outpout. /// </summary> /// <param name="effectStr">Effect information-</param> /// <param name="baseEffect">Base effect.</param> /// <param name="baseAttack">Base attack for the base effect.</param> /// <param name="baseEnemy">Base enemy for the base attack.</param> /// <returns>Converted data.</returns> public static string HandleEffect(string effectStr, Effect baseEffect, Attack baseAttack, Enemy baseEnemy) { /* What is wanted: * Damage: <span>(count*stack*amount)</span> over time (<span>amount</span> per hit). * Swiftness: (count*stack*amount) over time (amount per hit). * count*stack stability: for amount over time (stack per hit) * count*stack might: <span>count*stack</span>% more damage for amount over time (<span>stack</span>% more damage per hit) */ // Note: Listing per hit for might/stability may not make sense if there is only stack. // Keep the original string for error logging purposes. var original = string.Copy(effectStr); // Copy values from the base effect to avoid changing it. If this ever gets refactored feel free to use them directly. SubEffectInformation information = new SubEffectInformation(); information.hitCount = baseEffect.HitCount; information.hitLength = baseEffect.HitLength; information.hitFrequency = baseEffect.HitFrequency; information.variableHitCount = false; var split = effectStr.Split('|'); effectStr = split[0]; // Some effects get applied randomly. Don't add total damage/effect in those cases. Refactor this if possible. var effectChance = ""; if (split.Length > 1) effectChance = split[1]; if (effectChance.Length > 0) information.hitCount = 1; if (information.hitCount < 0) { information.variableHitCount = true; information.hitCount = 1; } // First effect type determines the icon. var firstType = ""; var firstStacks = 0; var startIcon = ""; var index = 0; while (true) { TagData text = TagData.FromString(effectStr, ':', ref index, new[] { ' ', '|', '(' }, new[] { ' ', '|', ')' }); if (text == null) break; // Ignore other stuff like enemies. if (text.Tag.Contains("=")) continue; if (text.Tag.Length == 0 || text.Data.Length == 0) { ErrorHandler.ShowWarningMessage("Enemy " + baseEnemy.Name + ": Something wrong with line '" + original + "'. Note: Use '\\:' instead of ':' in the text!"); effectStr = effectStr.Remove(index, 1); continue; } if (!SubEffect.EffectTypes.ContainsKey(text.Tag.ToLower())) { ErrorHandler.ShowWarningMessage("Skipping an effect. Effect " + text.Tag + " not recognized."); continue; } var subEffect = SubEffect.EffectTypes[text.Tag.ToLower()]; var effectType = subEffect.Name; // Effect info format varies based on tag (separated by :). information = ExtractInformation(information, subEffect, text, baseAttack, baseEnemy); var tag = SubEffect.GetTag(effectType); if (tag.Length > 0) baseEnemy.Tags.Add(tag); information = HandleStackingRules(information, subEffect); StringBuilder replace = GenerateReplace(information, subEffect, baseAttack); var toReplace = text.Tag + ":" + text.Data; effectStr = effectStr.Replace(toReplace, replace.ToString()); index = index - toReplace.Length + replace.Length; // Get the first subeffect type to display an icon. if (firstType.Equals("")) { if (information.icon.Equals("-") && effectType == EffectType.Buff) startIcon = "Buff"; else startIcon = "<span class=" + Constants.IconClass + " data-src=\"" + information.icon.ToLower() + "\" title=\"" + Helper.ToUpper(information.name.Replace('_', ' ')) + "\">" + Helper.ToUpper(information.name.Replace('_', ' ')) + "</span>"; firstType = information.name; firstStacks = information.stacks; } } return BuildHTML(firstType, startIcon, firstStacks, effectStr, effectChance).ToString(); }
/*********************************************************************************************** * HandleEffect / 2015-04-02 / Wethospu * * * * Converts raw effect data to final html output. * * Returns the converted output. * * * * effectStr: Raw effect data. * * weapon: Weapon slot for this skill. * * hitCount: How many times the attack part hits. * * hitLength: How long it takes for all hits to hit. * * hitFrequency: How often the effect tics. Only relevant for auras. * * baseEnemy: Enemy which owns this effect. Needed to add tags to the enemy. * * * ***********************************************************************************************/ private string HandleEffect(string effectStr, double coefficient, string weapon, int hitCount, double hitLength, double hitFrequency, Enemy baseEnemy) { /* What is wanted: * Damage: <span>(count*stack*amount)</span> over time (<span>amount</span> per hit). * Swiftness: (count*stack*amount) over time (amount per hit). * count*stack stability: for amount over time (stack per hit) * count*stack might: <span>count*stack</span>% more damage for amount over time (<span>stack</span>% more damage per hit) */ // Note: Listing per hit for might/stability may not make sense if there is only stack. / 2015-09-27 / Wethospu var original = string.Copy(effectStr); effectStr = Helper.ToUpper(LinkGenerator.CreatePageLinks(effectStr)); var split = effectStr.Split('|'); effectStr = split[0]; // Some effects get applied randomly. Don't add total damage/effect in those cases. / 2015-09-15 / Wethospu var effectChance = ""; if (split.Length > 1) effectChance = split[1]; if (effectChance.Length > 0) hitCount = 1; bool variableHitCount = false; if (hitCount < 0) { variableHitCount = true; hitCount = 1; } // First effect type determines the icon. / 2015-09-05 / Wethospu var firstType = ""; // The icon gets stack numbers. / 2015-09-05 / Wethospu var firstStacks = 0; var startIcon = ""; for (var index = Helper.FirstIndexOf(effectStr, new[] { ':' }); index < effectStr.Length && index > 0; index = Helper.FirstIndexOf(effectStr, new[] { ':' }, index + 1)) { // Effect format is 'tag':'info'. / 2015-08-09 / Wethospu var tagIndex = Helper.LastIndexOf(effectStr, new[] { ' ', '|', '(' }, index); var tag = effectStr.Substring(tagIndex + 1, index - tagIndex - 1); // Ignore other stuff like enemies. / 2015-08-09 / Wethospu if (tag.Contains("=")) continue; var effectIndex = Helper.FirstIndexOf(effectStr, new[] { ' ', '|', ')' }, index); // Check for valid ending characters. / 2015-08-09 / Wethospu if (effectStr[effectIndex - 1] == '!' || effectStr[effectIndex - 1] == ',' || effectStr[effectIndex - 1] == '.' || effectStr[effectIndex - 1] == ':') effectIndex--; if (effectIndex - index - 1 < 1) { Helper.ShowWarningMessage("Enemy " + baseEnemy.Name + ": Something wrong with line '" + original + "'. Note: ':' can't be used in text!"); effectStr = effectStr.Remove(index, 1); continue; } var effect = effectStr.Substring(index + 1, effectIndex - index - 1); // Effect info format varies based on tag (separated by :). / 2015-08-09 / Wethospu var effectData = effect.Split(':'); var effectType = GetEffectType(tag); var category = tag.ToLower(); var amount = 0.0; var duration = 0.0; var stacks = 1; var buff = ""; var icon = category; var stacksAdditively = true; var suffix = "damage"; if (effectType == EffectType.Damage || effectType == EffectType.DamageFixed || effectType == EffectType.DamagePercent) { if (effectData[0].Equals("-")) amount = coefficient; else amount = Helper.ParseD(effectData[0]); icon = "damage"; stacks = 1; } else if (effectType == EffectType.Healing || effectType == EffectType.HealingPercent) { amount = Helper.ParseD(effectData[0]); suffix = "healing"; icon = "healing"; stacks = 1; } if (effectType == EffectType.Boon|| effectType == EffectType.Condition || effectType == EffectType.Control) { baseEnemy.Tags.Add(category); } if (effectType == EffectType.Agony || effectType == EffectType.Boon || effectType == EffectType.Condition) { duration = Helper.ParseD(effectData[0]); if (effectType == EffectType.Agony || category.Equals("bleeding") || category.Equals("torment") || category.Equals("burning") || category.Equals("poison") || category.Equals("confusion") || category.Equals("regeneration") || category.Equals("might")) amount = duration; if (effectData.Length > 1) stacks = Helper.ParseI(effectData[1]); stacksAdditively = EffectStacksDuration(category); if (stacksAdditively || effectType == EffectType.Boon) suffix = "seconds"; if (category.Equals("regeneration")) suffix = "healing"; if (category.Equals("retaliation")) suffix = "damage per hit"; if(category.Equals("might")) suffix = "more damage"; if (category.Equals("retaliation") || category.Equals("might")) amount = 1; } if (effectType == EffectType.Control) { stacksAdditively = false; duration = Helper.ParseD(effectData[0]); if (effectData.Length > 1) stacks = Helper.ParseI(effectData[1]); } if (effectType == EffectType.Buff) { buff = effectData[0]; icon = buff; if (effectData.Length > 1 && effectData[1].Length > 0) duration = Helper.ParseD(effectData[1]); if (effectData.Length > 2 && effectData[2].Length > 0) stacks = Helper.ParseI(effectData[2]); if (effectData.Length > 3 && effectData[3].Length > 0) icon = effectData[3]; if (effectData.Length > 4) stacksAdditively = Helper.ParseI(effectData[4]) > 0 ? true : false; } var totalAmount = 0.0; var totalLength = 0.0; var totalDuration = 0.0; if (stacksAdditively) { totalAmount = hitCount * amount * stacks; totalDuration = hitCount * duration * stacks; totalLength = hitLength; // 3 different values makes the effect very confusing so just remove the hit length. / 2015-10-11 / Wethospu if (totalAmount > 0 && totalDuration > 0) totalLength = 0; stacks = 0; } else { amount = amount * stacks; totalAmount = hitCount * amount; totalDuration = 0; // Without damage don't do "over X seconds". Instead, just add the duration (makes control work properly). / 2015-10-01 / Wethospu if (amount == 0) { totalDuration = duration; duration = 0; } totalLength = hitLength + duration; stacks = hitCount * stacks; } AddTagsFromEffectType(effectType, baseEnemy); // Syntax: <span class="TAGValue">VALUE</span> var replace = new StringBuilder(); //// Put both total and damage per hit. / 2015-09-08 / Wethospu if (amount > 0) { // All amounts need clientside formatting. / 2015-09-27 / Wethospu // Add information as data values so it can be recalculated in the browser when enemy level changes. / 2015 - 09 - 27 / Wethospu replace.Append("<span class=\"").Append(EffectTypeToClass(effectType)).Append("\" data-effect=\"").Append(category); if (category.Equals("confusion")) replace.Append("1"); replace.Append("\" data-amount=\"").Append(totalAmount).Append("\" data-weapon=\"").Append(weapon).Append("\"></span>"); replace.Append(" ").Append(suffix); } if (totalDuration > 0) { if (amount > 0) replace.Append(" over "); replace.Append(totalDuration).Append(" second"); if (totalDuration != 1.0) replace.Append("s"); } replace.Append(HitLengthStr(totalLength)); if (hitCount > 1 && (amount > 0 || duration > 0)) { // Same as above but for a single hit. / 2015-09-27 / Wethospu replace.Append("<span class=\"secondary-info\"> ("); if (amount > 0) { // All amounts need clientside formatting. / 2015-09-27 / Wethospu // Add information as data values so it can be recalculated in the browser when enemy level changes. / 2015 - 09 - 27 / Wethospu replace.Append("<span class=\"").Append(EffectTypeToClass(effectType)).Append("\" data-effect=\"").Append(category); if (category.Equals("confusion")) replace.Append("1"); replace.Append("\" data-amount=\"").Append(amount).Append("\"></span>"); replace.Append(" ").Append(suffix); } if (duration > 0) { if (amount > 0) replace.Append(" over "); replace.Append(duration).Append(" second"); if (totalDuration != 1.0) replace.Append("s"); } replace.Append(" per hit)</span>"); } if (category.Equals("confusion")) { suffix = "damage per skill usage"; replace.Append(". "); if (amount > 0) { replace.Append("<span class=\"").Append(EffectTypeToClass(effectType)).Append("\" data-effect=\"").Append(category).Append("2"); replace.Append("\" data-amount=\"").Append(totalAmount).Append("\"></span>"); replace.Append(" ").Append(suffix); } if (duration > 0) { if (amount > 0) replace.Append(" over "); replace.Append(totalLength).Append(" second"); if (totalLength != 1.0) replace.Append("s"); } replace.Append(HitLengthStr(hitLength)); if (hitCount > 1) { replace.Append("<span class=\"secondary-info\"> ("); if (amount > 0) { replace.Append("<span class=\"").Append(EffectTypeToClass(effectType)).Append("\" data-effect=\"").Append(category).Append("2"); if (category.Equals("confusion")) replace.Append("1"); replace.Append("\" data-amount=\"").Append(amount).Append("\"></span>"); replace.Append(" ").Append(suffix); } if (duration > 0) { if (amount > 0) replace.Append(" over "); replace.Append(duration).Append(" second"); if (duration != 1.0) replace.Append("s"); } replace.Append(" per hit)</span>"); } } // Some effects can have variable or unknown hit count. Just add " per hit" in those cases. / 2015-09-29 / Wethospu if (variableHitCount) replace.Append(" per hit"); if (hitFrequency > 0.01) { replace.Append(" every ").Append(hitFrequency).Append(" second"); if (hitFrequency != 1.0) replace.Append("s"); } if (effectType == EffectType.Buff) { // Add the buff name (people probably won't recognize all icons). / 2015-09-23 / Wethospu replace.Append(" (").Append(buff.Replace('_', ' ')).Append(")"); } var toReplace = tag + ":" + effect; effectStr = effectStr.Replace(toReplace, replace.ToString()); // Get the first subeffect type to display an icon. / 2015-09-09 / Wethospu if (firstType.Equals("")) { if (icon.Equals("-") && effectType == EffectType.Buff) startIcon = "Buff"; else startIcon = "<span class=" + Constants.IconClass + " data-src=\"" + icon.ToLower() + "\" title=\"" + Helper.ToUpper(category.Replace('_', ' ')) + "\">" + Helper.ToUpper(category.Replace('_', ' ')) + "</span>"; firstType = category; firstStacks = stacks; } } // Syntax: <li>'stacks'<span class="icon" title="effectType'"></span>: 'effect'</li> var effectBuilder = new StringBuilder(); if (!firstType.Equals("")) { effectBuilder.Append(startIcon); if (firstStacks > 1) effectBuilder.Append("x").Append(firstStacks); if (effectStr.Length > 0) effectBuilder.Append(": "); } if (effectStr.Length > 0) { effectBuilder.Append(effectStr); if (effectBuilder.Length > 0 && effectBuilder[effectBuilder.Length - 1] != '.') effectBuilder.Append("."); if (firstType.Equals("torment")) effectBuilder.Append("<span class=\"secondary-info\"> Double damage when moving.</span>"); if (effectChance.Length > 0) effectBuilder.Append(" ").Append(effectChance).Append(" chance per hit."); } return effectBuilder.ToString(); }
/// <summary> /// Sets fractal scaling type for a given enemy. See GW2Helper.ScalingTypeToString for accepted values. /// </summary> private static void HandleScaling(string data, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"scaling='type'!"); currentEnemy.ScalingType = data; }
/*********************************************************************************************** * AddTagsFromEffectType / 2015-06-09 / Wethospu * * * * Adds necessary tags to given enemy based on effect type (used for search). * * Displays a warning message if the effect type is not implemented. * * * * type: Effect type which affects added tags. * * * ***********************************************************************************************/ private void AddTagsFromEffectType(EffectType type, Enemy baseEnemy) { if (type == EffectType.Agony) baseEnemy.Tags.Add("agony"); else if (type == EffectType.Boon) baseEnemy.Tags.Add("boon"); else if (type == EffectType.Buff) baseEnemy.Tags.Add("buff"); else if (type == EffectType.Condition) baseEnemy.Tags.Add("condition"); else if (type == EffectType.Control) baseEnemy.Tags.Add("control"); else if (type == EffectType.Damage) baseEnemy.Tags.Add("damage"); else if (type == EffectType.DamageFixed) baseEnemy.Tags.Add("fixed damage"); else if (type == EffectType.DamagePercent) baseEnemy.Tags.Add("percent damage"); else if (type == EffectType.Healing || type == EffectType.HealingPercent) baseEnemy.Tags.Add("healing"); else if (type != EffectType.None) Helper.ShowWarningMessage("Internal error. Effect type not implemented."); }
/// <summary> /// Similar to encounter tactics. Activates tactics for a given enemy. Activated tactics are able to receive content lines. /// </summary> private static void HandleTactic(string data, Enemy currentEnemy) { // Set validity to over max so custom tactics never get overridden by encounter tactics. currentEnemy.TacticValidity = 2.0; currentEnemy.HandleTactic(data, null); }
/// <summary> /// Adds custom search tags to a given enemy. Not really in use because most tags are handled automatically. /// </summary> private static void HandleTag(string data, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"tag='tag1'|'tag2'|'tag3'\"!"); var split = data.Split('|'); foreach (var str in split) currentEnemy.Tags.Add(str.ToLower()); }
/// <summary> /// Most toughness values are acquired automatically from datamined data. This is used for special cases. /// </summary> private static void HandleToughness(string data, Enemy currentEnemy) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"toughness='amount'."); currentEnemy.Attributes.Multipliers.Toughness = Helper.ParseD(data); if (currentEnemy.Attributes.Multipliers.Toughness > 100) ErrorHandler.ShowWarning("Toughness values should be multipliers. Calculate the multiplier."); }
/*********************************************************************************************** * GenerateEnemies / 2014-08-01 / Wethospu * * * * Generates enemies for one dungeon. * * * * Returns list of generated enemies. * * enemyAttributes: Datamined enemy attributes and other info. * * * ***********************************************************************************************/ public static List<Enemy> GenerateEnemies(Dictionary<string, EnemyAttributes> enemyAttributes) { var enemyData = new List<Enemy>(); if (!Directory.Exists(Constants.DataEnemyRaw)) { Helper.ShowWarning("Directory " + Constants.DataEnemyRaw + " doesn't exist."); return enemyData; } var enemyFiles = Directory.GetFiles(Constants.DataEnemyRaw); foreach (var file in enemyFiles) { if (Path.GetExtension(file) != ".txt") continue; string[] lines; if (File.Exists(file)) lines = File.ReadAllLines(file, Constants.Encoding); else { Helper.ShowWarningMessage("File " + file + " doesn't exist!"); return null; } Helper.CurrentFile = file; for (var row = 0; row < lines.Length; row++) { Helper.InitializeWarningSystem(row + 1, lines[row]); HandleLine(lines[row], enemyData, enemyAttributes); } } // Add the last enemy. if (_currentEffect != null && _currentAttack != null) _currentAttack.Effects.Add(_currentEffect); if (_currentAttack != null && _currentEnemy != null) _currentEnemy.Attacks.Add(_currentAttack); if (_currentEnemy != null) enemyData.Add(_currentEnemy); // Reset internal state. Helper.InitializeWarningSystem(-1, ""); _currentEnemy = null; _currentAttack = null; _currentEffect = null; // Sort for consistency (also allows see enemies without proper ids). / 2015-10-05 / Wethospu enemyData.Sort(); // Set up internal indexes. / 2015-10-05 / Wethospu for (var i = 0; i < enemyData.Count; i++) enemyData[i].Index = i; return enemyData; }
private static void HandleAdditional(string data, Attack currentAttack, Enemy currentEnemy) { // Treat additional information as an effect for a simpler UI. if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"additional='text'\"."); var lower = data.ToLower(); if (lower.Contains("can't be blocked") || lower.Contains("can't block")) { currentEnemy.Tags.Add("can't block"); currentAttack.CantBeBlocked = true; } if (lower.Contains("can't be evaded") || lower.Contains("can't evade")) currentEnemy.Tags.Add("can't evade"); currentAttack.Effects.Add(new Effect(LinkGenerator.CheckLinkSyntax(data))); }