/*********************************************************************************************** * 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; }
/*********************************************************************************************** * EffectLoop / 2014-08-01 / Wethospu * * * * Sub process loop for attack effects. * * * * tag: Tag of the line. * * data: Data of the line. * * * ***********************************************************************************************/ private static int EffectLoop(string tag, string data) { // Add old effect and start a new one. if (tag.Equals("effect")) { if (data.Length == 0) Helper.ShowWarning("Missing info. Use \"effect='type'\"!"); if (_currentEffect != null) _currentAttack.Effects.Add(_currentEffect); var type = data.ToLower(); foreach (var enemyTag in Constants.AttackTypeTags) { if (type.Contains(enemyTag)) _currentEnemy.Tags.Add(enemyTag); } _currentEffect = new Effect(LinkGenerator.CreatePageLinks(LinkGenerator.CheckLinkSyntax(data))); } // Tag from attack loop. Save effect and exit this loop. else if (tag.Equals("attack")) { if (_currentEffect != null) _currentAttack.Effects.Add(_currentEffect); _currentEffect = null; return -1; } // Tag from main loop. Save both effect and attack and then exit this loop. else if (tag.Equals("name") || tag.Equals("potion") || tag.Equals("copy")) { if (_currentEffect != null) { _currentAttack.Effects.Add(_currentEffect); _currentEnemy.Attacks.Add(_currentAttack); } _currentEffect = null; _currentAttack = null; return -2; } else if (tag.Equals("count")) { if (data.Length > 0) { if (data.Equals("?")) _currentEffect.HitCount = -1; else _currentEffect.HitCount = Helper.ParseI(data); if (_currentEffect.HitCount == 0) Helper.ShowWarning("Hit count can't be zero."); } else Helper.ShowWarning("Missing info. Use \"count='number'\""); } else if (tag.Equals("length")) { if (data.Length > 0) _currentEffect.HitLength = Helper.ParseD(data); else Helper.ShowWarning("Missing info. Use \"length='number'\""); } else if (tag.Equals("frequency")) { if (data.Length > 0) _currentEffect.HitFrequency = Helper.ParseD(data); else Helper.ShowWarning("Missing info. Use \"frequency='number'\""); } // Add subeffects to the effect. else if (tag.Equals("subeffect")) { if (data.Length > 0) { data = LinkGenerator.CheckLinkSyntax(data); _currentEffect.SubEffects.Add(data); } else Helper.ShowWarning("Missing info. Use TODO!"); } // Error handling for wrongly placed tags. else if (tag.Equals("additional") || tag.Equals("cooldown") || tag.Equals("animation")) Helper.ShowWarning("Wrong position for tag " + tag + ". Move above any type-tags."); else if (tag.Equals("")) Helper.ShowWarning("Something wrong with line " + data + "."); else Helper.ShowWarning("Unrecognized tag: " + tag); return 0; }
/*********************************************************************************************** * 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; }
/*********************************************************************************************** * AttackLoop / 2014-08-01 / Wethospu * * * * Sub process loop for enemy attacks. * * * * tag: Tag of the line. * * data: Data of the line. * * * ***********************************************************************************************/ static int AttackLoop(string tag, string data) { // Add old attack and start a new one. if (tag.Equals("attack")) { if (data.Length == 0) Helper.ShowWarning("Missing info. Use \"attack='name'\"!"); if (_currentAttack != null) _currentEnemy.Attacks.Add(_currentAttack); _currentAttack = new Attack(LinkGenerator.CreatePageLinks(LinkGenerator.CheckLinkSyntax(data))); _currentEffect = null; } // Tags from main loop. Save attack and exit this loop. else if (tag.Equals("name") || tag.Equals("copy") || tag.Equals("potion")) { if (_currentAttack != null) _currentEnemy.Attacks.Add(_currentAttack); _currentAttack = null; _currentEffect = null; return -1; } else if (tag.Equals("id")) { if (data.Length > 0) { _currentAttack.LoadAttributes(Helper.ParseI(data), _currentEnemy.Attributes); } else Helper.ShowWarning("Missing info. Use \"id=number\"."); } // Tags from effect loop. Exit immediately. else if (tag.Equals("effect")) { return 1; } else if (tag.Equals("cooldown")) { if (data.Length > 0) _currentAttack.Cooldown = Helper.ParseD(data); else Helper.ShowWarning("Missing info. Use \"cooldown='number'\"."); } else if (tag.Equals("additional")) { // Treat additional as an effect. / 2015-09-22 / Wethospu if (data.Length == 0) Helper.ShowWarning("Missing info. Use \"additional='text'\"."); if (_currentEffect != null) _currentAttack.Effects.Add(_currentEffect); var lower = data.ToLower(); // Check for interesting tags. / 2015-09-22 / Wethospu if (lower.Contains("can't be blocked") || lower.Contains("can't block")) _currentEnemy.Tags.Add("can't block"); if (lower.Contains("can't be evaded") || lower.Contains("can't evade")) _currentEnemy.Tags.Add("can't evade"); _currentEffect = new Effect(LinkGenerator.CreatePageLinks(LinkGenerator.CheckLinkSyntax(data))); } else if (tag.Equals("animation")) { if (data.Length > 0) { if (data.Contains(':') && !data.Contains('=')) Helper.ShowWarning("Potentially use of wrong syntax. Use \"animation='pre cast'|'time'|'after cast'\" !"); _currentAttack.Animation = data; } else Helper.ShowWarning("Missing info. Use \"animation='pre cast'|'time'|'after cast'\" !"); } else if (tag.Equals("image")) { if (data.Length > 0) { _currentAttack.Medias.Add(new Media(data)); } else Helper.ShowWarning("Missing info. Use \"image='imagelink'\"!"); } else if (tag.Equals("subeffect")) Helper.ShowWarning("Missing attack effect (\"effect='type'\")!"); else if (tag.Equals("")) Helper.ShowWarning("Something wrong with line " + data + "."); else Helper.ShowWarning("Unrecognized tag: " + tag); return 0; }
/// <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(); }
private static void HandleSubeffect(string data, Effect currentEffect) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use TODO!"); currentEffect.SubEffects.Add(LinkGenerator.CheckLinkSyntax(data)); }
private static void HandleLength(string data, Effect currentEffect) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"length='number'\""); currentEffect.HitLength = Helper.ParseD(data); }
private static void HandleCount(string data, Effect currentEffect) { if (data.Length == 0) ErrorHandler.ShowWarning("Missing info. Use \"count='number'\""); if (data.Equals("?")) currentEffect.HitCount = -1; else currentEffect.HitCount = Helper.ParseI(data); if (currentEffect.HitCount == 0) ErrorHandler.ShowWarning("Hit count can't be zero."); }