private static string WriteSpecialAbility(HeroLabCharacter character, HeroLabSpecial specialAbilities) { var stringBuilder = new StringBuilder($"!HeroLabImporter --name {character.Name} --mode add --addtype abilities"); stringBuilder.Append($" --set name {specialAbilities.Name}"); stringBuilder.Append($" --set type {GetSpecialAbilityType(specialAbilities.Type)}"); stringBuilder.Append($" --set descflag 1"); stringBuilder.Append($" --set options-flag 0"); stringBuilder.Append($" --set description {OneLineString(specialAbilities.Description.Replace('\r', ' ').Replace('\n', ' ').Trim())}"); return(stringBuilder.ToString()); }
private static string WriteNpcSpellLikeAbility(HeroLabCharacter character, HeroLabSpecial spellLikeAbility) { var stringBuilder = new StringBuilder($"!HeroLabImporter --name {character.Name} --mode add --addtype spell-like"); if (spellLikeAbility.Name.ToLower().Contains("(constant)")) { stringBuilder.Append(" --set timesperday constant"); } else if (spellLikeAbility.Name.ToLower().Contains("(at will)")) { stringBuilder.Append(" --set timesperday at-will"); } else if (spellLikeAbility.Name.ToLower().Contains("/day")) { stringBuilder.Append(" --set timesperday per-day"); } else if (spellLikeAbility.Name.ToLower().Contains("/hour")) { stringBuilder.Append(" --set timesperday per-hour"); } else if (spellLikeAbility.Name.ToLower().Contains("/week")) { stringBuilder.Append(" --set timesperday per-week"); } else if (spellLikeAbility.Name.ToLower().Contains("/month")) { stringBuilder.Append(" --set timesperday per-month"); } else if (spellLikeAbility.Name.ToLower().Contains("/year")) { stringBuilder.Append(" --set timesperday per-year"); } var trackedResource = character.TrackedResources.Items.FirstOrDefault(tr => tr.Name == spellLikeAbility.Name); if (trackedResource != null) { stringBuilder.Append($" --set perday_max {trackedResource.Max}"); } stringBuilder.Append($" --set spellname {spellLikeAbility.ShortName}"); stringBuilder.Append($" --set spelldesc {spellLikeAbility.Description.Replace('\r', ' ').Replace('\n', ' ')}"); var spell = SpellDatabase.Instance.FirstOrDefault(ss => ss.Name == spellLikeAbility.ShortName); WriteNpcSpellInfo(stringBuilder, character, spell, 2); return(stringBuilder.ToString()); }
private static string WriteNpcWeapon(string type, HeroLabCharacter character, Weapon melee) { var stringBuilder = new StringBuilder($"!HeroLabImporter --name {character.Name} --mode add --addtype npcatk-{type} --set options-flag on"); stringBuilder.Append($" --set atkname {melee.Name}"); stringBuilder.Append($" --set multipleatk {GetNpcWeaponMultipleAttackFlag(melee)}"); var attackDisplay = melee.Name + " +"; if (melee.Attack.Contains("/")) { stringBuilder.Append($" --set multipleatk_flag 1"); var attackNumber = 1; foreach (var attack in melee.Attack.Split('/')) { if (attackNumber == 1) { attackDisplay += GetValue(attack); } else { attackDisplay += $"/{GetValue(attack)}"; } stringBuilder.Append(attackNumber == 1 ? $" --set atkmod {GetValue(attack)}" : $" --set atkmod{attackNumber} {GetValue(attack)}"); attackNumber++; } } else { attackDisplay += GetValue(melee.Attack); stringBuilder.Append($" --set atkmod {GetValue(melee.Attack)}"); } var critReplace = melee.Crit?.Replace("×", "x").Replace("/x2", "") ?? ""; if (string.IsNullOrEmpty(melee.Damage)) { stringBuilder.Append(" --set dmgflag 0"); } else if (!melee.Damage.Contains(" ")) { stringBuilder.Append(" --set dmgflag 1"); stringBuilder.Append($" --set dmgbase {melee.Damage}"); stringBuilder.Append(WriteNpcCrit(melee.Crit)); attackDisplay += CritDisplay(melee.Damage, "", critReplace); } else { var damage = melee.Damage.Split(' ')[0]; var plus = melee.Damage.Split(new[] { " plus " }, StringSplitOptions.None)[1]; if (Regex.IsMatch(plus, @"\d+d\d+")) { var plusDamage = Regex.Match(plus, @"\d+d\d+").Value; var plusExtra = plus.Replace(plusDamage, "").Trim(); stringBuilder.Append($" --set dmgbase {damage}"); stringBuilder.Append(" --set dmgflag 1"); stringBuilder.Append($" --set dmg2base {plusDamage}"); stringBuilder.Append($" --set dmg2type {plusExtra}"); stringBuilder.Append(" --set dmg2flag 1"); attackDisplay += CritDisplay(damage, plusDamage, critReplace); } else { stringBuilder.Append(" --set dmgflag 1"); stringBuilder.Append($" --set dmgbase {damage}"); stringBuilder.Append($" --set dmgtype {plus}"); attackDisplay += CritDisplay(damage, plus, critReplace); } stringBuilder.Append(WriteNpcCrit(melee.Crit)); } stringBuilder.Append($" --set atkdisplay {attackDisplay}"); if (type == "ranged" && melee.RangedAttack != null) { stringBuilder.Append($" --set atkrange {melee.RangedAttack.RangeIncrementText}"); } stringBuilder.Append(" --set options-flag 0"); return(stringBuilder.ToString()); }
private static string WriteNpcSpecialAttack(HeroLabCharacter character, HeroLabSpecial special) { var stringBuilder = new StringBuilder($"!HeroLabImporter --name {character.Name} --mode add --addtype npcatk-special"); stringBuilder.Append($" --set atkname {special.ShortName}"); stringBuilder.Append($" --set atkdisplay {special.ShortName}"); stringBuilder.Append($" --set options-flag 0"); var trackedResource = character.TrackedResources?.Items?.FirstOrDefault(tr => tr.Name == special.Name); if (trackedResource != null) { stringBuilder.Append($" --set perday 0"); stringBuilder.Append($" --set perday!max {trackedResource.Max}"); } stringBuilder.Append($" --set atkdesc {OneLineString(special.Description)}"); //First, we need to find the item that contains information on how this thing works. var checkers = Regex.Matches(special.Name, @"(?<=\().+?(?=\))"); bool hasAttack = false; bool hasRange = false; bool hasArea = false; bool hasEffect = false; bool hasSavingThrow = false; bool hasSavingThrowDc = false; bool hasDamage = false; bool hasDamage2 = false; string saveDc = null; string saveText = null; foreach (Match check in checkers) { if (check.Value.Contains("DC") || check.Value.Contains("ft.") || check.Value.Contains("rounds") || check.Value.Contains("rnd")) { var checkItems = check.Value.Split(new[] { ',', ';' }); foreach (var item in checkItems) { if (item.ToLower().Contains("cone") && !hasRange) { stringBuilder.Append(" --set rangeflag " + PreventRollingString("{{range=@{atkrange}}}")); stringBuilder.Append($" --set atkrange {item}"); hasRange = true; } else if (item.ToLower().Contains("dc") && !hasSavingThrowDc) { var dc = Regex.Match(item, @"\d+"); if (dc.Success) { saveDc = dc.Value; stringBuilder.Append($" --set atkdc {dc.Value}"); hasSavingThrowDc = true; if (hasSavingThrow) { stringBuilder.Append(" --set atksaveflag " + PreventRollingString("{{save=1}}{{savedc=@{atkdc}}}{{saveeffect=@{atksave}}}")); } } if (!hasSavingThrow) { var desc = special.Description.ToLower(); var modDesc = ""; if (desc.Contains("half") || desc.Contains("halves")) { modDesc = "half"; } else if (desc.Contains("partial")) { modDesc = "partial"; } else if (desc.Contains("negate")) { modDesc = "negates"; } var checkDesc = ""; if (desc.Contains("fortitude")) { checkDesc = "Fortitude"; } if (desc.Contains("reflex")) { checkDesc = "Reflex"; } if (desc.Contains("will")) { checkDesc = "Will"; } if (!string.IsNullOrEmpty(modDesc) && !string.IsNullOrEmpty(checkDesc)) { stringBuilder.Append($" --set atksave {checkDesc} {modDesc}"); saveText = $"{checkDesc} {modDesc}"; hasSavingThrow = true; if (hasSavingThrowDc) { stringBuilder.Append(" --set atksaveflag " + PreventRollingString("{{save=1}}{{savedc=@{atkdc}}}{{saveeffect=@{atksave}}}")); } } } } else if (item.ToLower().Contains("feet") && !hasRange) { stringBuilder.Append(" --set rangeflag " + PreventRollingString("{{range=@{atkrange}}}")); stringBuilder.Append($" --set atkrange {item}"); hasRange = true; } else if ((item.ToLower().Contains("fire") || item.ToLower().Contains("sonic") || item.ToLower().Contains("acid") || item.ToLower().Contains("cold") || item.ToLower().Contains("sonic")) && !hasDamage) { var possibleDamage = Regex.Match(item.ToLower(), @"\d+d\d+[+-]?\d*"); if (possibleDamage.Success) { stringBuilder.Append(" --set dmgflag1 1"); stringBuilder.Append(" --set dmgflag " + PreventRollingString("{{damage=1}}{{dmg1flag=1}}{{dmg1=[[@{dmgbase}[MOD]+@{rollmod_damage}[QUERY]]]}}{{dmg1type=@{dmgtype}}}{{dmg1crit=[[(@{dmgbase}[MOD]+@{rollmod_damage}[QUERY])*@{dmgcritmulti}]]}}")); //stringBuilder.Append(" --set dmgflag "+ PreventRollingString("{{damage=1}}{{dmg1flag=1}}{{dmg1=[[" + possibleDamage.Value + "]]}}{{dmg1type=" + item.ToLower().Replace(possibleDamage.Value, "").Trim() + "}}{{dmg1crit=[[(" + possibleDamage.Value + ")*2]]}}")); stringBuilder.Append($" --set dmgbase {possibleDamage.Value}"); stringBuilder.Append( $" --set dmgtype {item.ToLower().Replace(possibleDamage.Value, "").Trim()}"); hasDamage = true; } } else if ((item.ToLower().Contains("fire") || item.ToLower().Contains("sonic") || item.ToLower().Contains("acid") || item.ToLower().Contains("cold") || item.ToLower().Contains("sonic")) && !hasDamage2) { var possibleDamage = Regex.Match(item.ToLower(), @"\d+d\d+[+-]?\d*"); if (possibleDamage.Success) { stringBuilder.Append(" --set dmgflag2 " + PreventRollingString("{{damage=1}}{{dmg2flag=1}}{{dmg2=[[@{dmg2base}[MOD]+@{rollmod_damage}[QUERY]]]}}{{dmg2type=@{dmg2type}}}{{dmg1crit=[[(@{dmg2base}[MOD]+@{rollmod_damage}[QUERY])*@{dmg2critmulti}]]}}")); //stringBuilder.Append(" --set dmgflag2 " + PreventRollingString("{{damage=1}}{{dmg2flag=1}}{{dmg2=[[" + possibleDamage.Value + "]]}}{{dmg2type=" + item.ToLower().Replace(possibleDamage.Value, "").Trim() + "}}{{dmg2crit=[[(" + possibleDamage.Value + ")]]}}")); stringBuilder.Append($" --set dmgbase2 {possibleDamage.Value}"); stringBuilder.Append( $" --set dmgtype2 {item.ToLower().Replace(possibleDamage.Value, "").Trim()}"); hasDamage2 = true; } } else if ((item.ToLower().Contains("half") || item.ToLower().Contains("partial") || item.ToLower().Contains("negates")) && !hasSavingThrow) { var fixedItem = Regex.Replace(item.ToLower(), @"dc \d*", "").Trim(); hasSavingThrow = true; stringBuilder.Append($" --set atksave {fixedItem}"); saveText = fixedItem; if (hasSavingThrowDc) { stringBuilder.Append(" --set atksaveflag " + PreventRollingString("{{save=1}}{{savedc=@{atkdc}}}{{saveeffect=@{atksave}}}")); } } else if (item.ToLower().Contains("every") && !hasEffect) { var finalItem = item.ToLower(); var replacements = Regex.Matches(item.ToLower(), @"\d+d\d+[+-]?\d*"); foreach (Match match in replacements) { finalItem = finalItem.Replace(match.Value, $"[[{match.Value}]]"); } stringBuilder.Append($" --set atkeffect {finalItem}"); stringBuilder.Append(" --set effectflag " + PreventRollingString("{{effect=@{atkeffect}}}")); hasEffect = true; } } } } stringBuilder.Append(" --set options-flag 0"); return(stringBuilder.ToString()); }
private static string WriteFeat(HeroLabCharacter character, Feat feat) { var stringBuilder = new StringBuilder($"!HeroLabImporter --name {character.Name} --mode add --addtype feats"); stringBuilder.Append($" --set name {feat.Name}"); //name stringBuilder.Append($" --set type {(string.IsNullOrEmpty(feat.CategoryText) ? "general" : feat.CategoryText.ToLower())}"); //type stringBuilder.Append($" --set options-flag 0"); var description = feat.Description; var prerequisites = string.Empty; var benefits = string.Empty; var normal = string.Empty; var special = string.Empty; const string prerequisiteRegex = "Prerequisites?:.+"; const string benefitRegex = "Benefits?:.+"; const string normalRegex = "Normal:.+"; const string specialRegex = "Special:.+"; if (Regex.IsMatch(description, prerequisiteRegex, RegexOptions.IgnoreCase)) { prerequisites = Regex.Match(description, prerequisiteRegex, RegexOptions.IgnoreCase).Value; description = description.Replace(prerequisites, ""); prerequisites = prerequisites.Split(':')[1]; } if (Regex.IsMatch(description, benefitRegex, RegexOptions.IgnoreCase)) { benefits = Regex.Match(description, benefitRegex, RegexOptions.IgnoreCase).Value; description = description.Replace(benefits, ""); benefits = benefits.Split(':')[1]; } if (Regex.IsMatch(description, normalRegex, RegexOptions.IgnoreCase)) { normal = Regex.Match(description, normalRegex, RegexOptions.IgnoreCase).Value; description = description.Replace(normal, ""); normal = normal.Split(':')[1]; } if (Regex.IsMatch(description, specialRegex, RegexOptions.IgnoreCase)) { special = Regex.Match(description, specialRegex, RegexOptions.IgnoreCase).Value; description = description.Replace(special, ""); special = special.Split(':')[1]; } if (!string.IsNullOrEmpty(prerequisites.Trim()) && prerequisites.Length > 1) { stringBuilder.Append($" --set prerequisites {OneLineString(prerequisites)}"); //prerequisites } if (!string.IsNullOrEmpty(benefits.Trim()) && benefits.Length > 1) { stringBuilder.Append($" --set benefits {OneLineString(benefits)}"); //benefits } if (!string.IsNullOrEmpty(normal.Trim()) && normal.Length > 1) { stringBuilder.Append($" --set normal {OneLineString(normal)}"); //normal } if (!string.IsNullOrEmpty(special.Trim()) && special.Length > 1) { stringBuilder.Append($" --set special {OneLineString(special)}"); //special } if (!string.IsNullOrEmpty(description.Trim()) && description.Length > 1) { stringBuilder.Append(" --set descflag 1"); //descflag stringBuilder.Append($" --set description {OneLineString(description)}"); // description } return(stringBuilder.ToString()); }
private static void ProcessNpcCharacter(HeroLabCharacter character) { var items = new List <string>(); var mainCharacter = new StringBuilder("!HeroLabImporter --mode clear --set npc 1 --set options-flag-npc 0"); mainCharacter.Append($" --name {character.Name}"); WriteNpcMain(mainCharacter, character); WriteNpcDefense(mainCharacter, character); WriteNpcOffense(mainCharacter, character); WriteNpcStatistics(mainCharacter, character); items.Add(mainCharacter.ToString()); //Melee Weapons if (character.Melee?.Items?.Any() == true) { foreach (var melee in character.Melee.Items) { var item = WriteNpcWeapon("melee", character, melee); if (!string.IsNullOrEmpty(item)) { items.Add(item); } } } //Ranged Weapons if (character.Ranged?.Items?.Any() == true) { foreach (var ranged in character.Ranged.Items) { var item = WriteNpcWeapon("ranged", character, ranged); if (!string.IsNullOrEmpty(item)) { items.Add(item); } } } //Special Attacks if (character.Attack?.Items?.Any() == true) { foreach (var sa in character.Attack.Items) { var item = WriteNpcSpecialAttack(character, sa); if (!string.IsNullOrEmpty(item)) { items.Add(item); } } } //Spell-Like Abilities if (character.SpellLikeAbilities?.Items?.Any() == true) { foreach (var spellLikeAbility in character.SpellLikeAbilities.Items) { var item = WriteNpcSpellLikeAbility(character, spellLikeAbility); if (!string.IsNullOrEmpty(item)) { items.Add(item); } } } //Spells if (character.SpellsMemorized?.Items?.Any() == true) { foreach (var spell in character.SpellsMemorized.Items) { var item = WriteNpcSpell(character, spell); if (!string.IsNullOrEmpty(item)) { items.Add(item); } } } //Feats if (character.Feats?.Items?.Any() == true) { foreach (var feat in character.Feats.Items) { var item = WriteFeat(character, feat); if (!string.IsNullOrEmpty(item)) { items.Add(item); } } } //Special Abilities if (character.OtherSpecials?.Items?.Any() == true) { foreach (var specialAbilities in character.OtherSpecials.Items) { var item = WriteSpecialAbility(character, specialAbilities); if (!string.IsNullOrEmpty(item)) { items.Add(item); } } } using (var file = File.Open($"C:\\PathfinderExporter\\{character.Name}_NPC.txt", FileMode.Create, FileAccess.Write, FileShare.Read)) using (var fileWriter = new StreamWriter(file) { AutoFlush = true }) { foreach (var line in items) { if (line == items.Last()) { fileWriter.Write(line); } else { fileWriter.WriteLine(line); } } } }