diff --git a/AideDeJeu/AideDeJeu/Tools/MarkdownExtensions.cs b/AideDeJeu/AideDeJeu/Tools/MarkdownExtensions.cs new file mode 100644 index 00000000..f0fc615a --- /dev/null +++ b/AideDeJeu/AideDeJeu/Tools/MarkdownExtensions.cs @@ -0,0 +1,445 @@ +using AideDeJeuLib.Spells; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Linq; +using System.Diagnostics; +using AideDeJeuLib.Monsters; + +namespace AideDeJeu.Tools +{ + public static class MarkdownExtensions + { + public static IEnumerable ToSpells(this Markdig.Syntax.MarkdownDocument document) + { + var spells = new List(); + Spell spell = null; + foreach (var block in document) + { + //DumpBlock(block); + if (block is Markdig.Syntax.HeadingBlock) + { + var headingBlock = block as Markdig.Syntax.HeadingBlock; + //DumpHeadingBlock(headingBlock); + if (headingBlock.HeaderChar == '#' && headingBlock.Level == 1) + { + if (spell != null) + { + spells.Add(spell); + } + spell = new Spell(); + spell.Name = spell.NamePHB = headingBlock.Inline.ToContainerString(); + //Console.WriteLine(spell.Name); + } + } + if (block is Markdig.Syntax.ParagraphBlock) + { + var paragraphBlock = block as Markdig.Syntax.ParagraphBlock; + spell.DescriptionHtml += paragraphBlock.ToParagraphString(); + ////DumpParagraphBlock(paragraphBlock); + //Console.WriteLine(paragraphBlock.IsBreakable); + //spell.DescriptionHtml += paragraphBlock.Inline.ToContainerString(); + //if(paragraphBlock.IsBreakable) + //{ + // spell.DescriptionHtml += "\n"; + //} + } + if (block is Markdig.Syntax.ListBlock) + { + var listBlock = block as Markdig.Syntax.ListBlock; + //DumpListBlock(listBlock); + if (listBlock.BulletType == '-') + { + spell.Source = ""; + foreach (var inblock in listBlock) + { + //DumpBlock(inblock); + var regex = new Regex("(?.*?): (?.*)"); + if (inblock is Markdig.Syntax.ListItemBlock) + { + var listItemBlock = inblock as Markdig.Syntax.ListItemBlock; + foreach (var ininblock in listItemBlock) + { + //DumpBlock(ininblock); + if (ininblock is Markdig.Syntax.ParagraphBlock) + { + var paragraphBlock = ininblock as Markdig.Syntax.ParagraphBlock; + //DumpParagraphBlock(paragraphBlock); + var str = paragraphBlock.Inline.ToContainerString(); + var match = regex.Match(str); + var key = match.Groups["key"].Value; + var value = match.Groups["value"].Value; + switch (key) + { + case "NameVO": + spell.NameVO = value; + break; + case "CastingTime": + spell.CastingTime = value; + break; + case "Components": + spell.Components = value; + break; + case "Duration": + spell.Duration = value; + break; + case "LevelType": + spell.LevelType = value; + break; + case "Range": + spell.Range = value; + break; + case "Source": + spell.Source += value + " "; + break; + case "Classes": + spell.Source += value; + break; + } + } + } + + //DumpListItemBlock(inblock as Markdig.Syntax.ListItemBlock); + } + } + } + else + { + foreach (var inblock in listBlock) + { + if (inblock is Markdig.Syntax.ListItemBlock) + { + var listItemBlock = inblock as Markdig.Syntax.ListItemBlock; + foreach (var ininblock in listItemBlock) + { + //DumpBlock(ininblock); + if (ininblock is Markdig.Syntax.ParagraphBlock) + { + var paragraphBlock = ininblock as Markdig.Syntax.ParagraphBlock; + spell.DescriptionHtml += listBlock.BulletType + " " + paragraphBlock.ToParagraphString(); + } + } + } + } + } + } + + } + if (spell != null) + { + spells.Add(spell); + } + return spells; + } + + public static IEnumerable ToMonsters(this Markdig.Syntax.MarkdownDocument document) + { + var monsters = new List(); + Monster monster = null; + List actions = new List(); + foreach (var block in document) + { + //DumpBlock(block); + if (block is Markdig.Syntax.HeadingBlock) + { + var headingBlock = block as Markdig.Syntax.HeadingBlock; + //DumpHeadingBlock(headingBlock); + if (headingBlock.HeaderChar == '#' && headingBlock.Level == 1) + { + if (monster != null) + { + monsters.Add(monster); + } + monster = new Monster(); + monster.Name = monster.NamePHB = headingBlock.Inline.ToContainerString(); + //Console.WriteLine(spell.Name); + } + } + if (block is Markdig.Syntax.ParagraphBlock) + { + var paragraphBlock = block as Markdig.Syntax.ParagraphBlock; + actions.Add(paragraphBlock.ToParagraphString()); + ////DumpParagraphBlock(paragraphBlock); + //Console.WriteLine(paragraphBlock.IsBreakable); + //spell.DescriptionHtml += paragraphBlock.Inline.ToContainerString(); + //if(paragraphBlock.IsBreakable) + //{ + // spell.DescriptionHtml += "\n"; + //} + } + if (block is Markdig.Syntax.ListBlock) + { + var listBlock = block as Markdig.Syntax.ListBlock; + //DumpListBlock(listBlock); + if (listBlock.BulletType == '-') + { + monster.Source = ""; + foreach (var inblock in listBlock) + { + //DumpBlock(inblock); + var regex = new Regex("(?.*?): (?.*)"); + if (inblock is Markdig.Syntax.ListItemBlock) + { + var listItemBlock = inblock as Markdig.Syntax.ListItemBlock; + foreach (var ininblock in listItemBlock) + { + //DumpBlock(ininblock); + if (ininblock is Markdig.Syntax.ParagraphBlock) + { + var paragraphBlock = ininblock as Markdig.Syntax.ParagraphBlock; + //DumpParagraphBlock(paragraphBlock); + var str = paragraphBlock.Inline.ToContainerString(); + var match = regex.Match(str); + var key = match.Groups["key"].Value; + var value = match.Groups["value"].Value; + switch (key) + { + case "NameVO": + monster.NameVO = value; + break; + case "CastingTime": + { + var regexx = new Regex("(?.*) de taille (?.*), (?.*)"); + var matchh = regexx.Match(value); + monster.Alignment = matchh.Groups["alignment"].Value; + monster.Size = matchh.Groups["size"].Value; + monster.Type = matchh.Groups["type"].Value; + } + break; + case "ArmorClass": + monster.ArmorClass = value; + break; + case "HitPoints": + monster.HitPoints = value; + break; + case "Speed": + monster.Speed = value; + break; + case "SavingThrows": + monster.SavingThrows = value; + break; + case "Skills": + monster.Skills = value ; + break; + case "Senses": + monster.Senses = value; + break; + case "Languages": + monster.Languages = value; + break; + case "Challenge": + monster.Challenge = value; + break; + } + } + } + + //DumpListItemBlock(inblock as Markdig.Syntax.ListItemBlock); + } + } + } + else + { + foreach (var inblock in listBlock) + { + if (inblock is Markdig.Syntax.ListItemBlock) + { + var listItemBlock = inblock as Markdig.Syntax.ListItemBlock; + foreach (var ininblock in listItemBlock) + { + //DumpBlock(ininblock); + if (ininblock is Markdig.Syntax.ParagraphBlock) + { + var paragraphBlock = ininblock as Markdig.Syntax.ParagraphBlock; + actions.Add(listBlock.BulletType + " " + paragraphBlock.ToParagraphString()); + } + } + } + } + } + } + + } + if (monster != null) + { + monsters.Add(monster); + } + return monsters; + } + + public static string ToString(this Markdig.Syntax.SourceSpan span, string md) + { + return md.Substring(span.Start, span.Length); + } + public static string ToContainerString(this Markdig.Syntax.Inlines.ContainerInline inlines) + { + var str = string.Empty; + foreach (var inline in inlines) + { + Debug.WriteLine(inline.GetType()); + string add = string.Empty; + if (inline is Markdig.Syntax.Inlines.LineBreakInline) + { + add = "\n"; + } + else if (inline is Markdig.Syntax.Inlines.LiteralInline) + { + var literalInline = inline as Markdig.Syntax.Inlines.LiteralInline; + add = literalInline.Content.ToString(); + } + else if (inline is Markdig.Syntax.Inlines.EmphasisInline) + { + var emphasisInline = inline as Markdig.Syntax.Inlines.EmphasisInline; + var delimiterChar = emphasisInline.DelimiterChar.ToString(); + if (emphasisInline.IsDouble) + { + delimiterChar += delimiterChar; + } + add = delimiterChar + emphasisInline.ToContainerString() + delimiterChar; + } + else if (inline is Markdig.Syntax.Inlines.ContainerInline) + { + var containerInline = inline as Markdig.Syntax.Inlines.ContainerInline; + add = containerInline.ToContainerString(); + } + else + { + add = inline.ToString(); + } + Debug.WriteLine(add); + str += add; + } + return str; + } + public static string ToParagraphString(this Markdig.Syntax.ParagraphBlock paragraphBlock) + { + var str = string.Empty; + str += paragraphBlock.Inline.ToContainerString(); + if (paragraphBlock.IsBreakable) + { + str += "\n"; + } + return str; + } + + public static string ToMarkdownString(this IEnumerable spells) + { + var md = string.Empty; + foreach (var spell in spells) + { + md += spell.ToMarkdownString(); + } + return md; + } + public static string ToMarkdownString(this Spell spell) + { + var md = string.Empty; + md += string.Format("# {0}\n", spell.NamePHB); + md += string.Format("- NameVO: {0}\n", spell.NameVO); + md += string.Format("- CastingTime: {0}\n", spell.CastingTime); + md += string.Format("- Components: {0}\n", spell.Components); + md += string.Format("- Duration: {0}\n", spell.Duration); + md += string.Format("- LevelType: {0}\n", spell.LevelType); + md += string.Format("- Range: {0}\n", spell.Range); + var regex = new Regex("(?\\(.*\\)) (?.*)"); + var match = regex.Match(spell.Source); + var source = match.Groups["source"].Value; + var classes = match.Groups["classes"].Value; + md += string.Format("- Source: {0}\n", source); + md += string.Format("- Classes: {0}\n", classes.Replace(" ;", ",").Trim().Trim(',')); + md += "\n"; + md += "### Description\n\n"; + md += spell + .DescriptionHtml + .Replace("", "**") + .Replace("", "**") + .Replace("", "_") + .Replace("", "_") + .Replace("
  • ", "* ") + .Replace("
  • ", "") + .Replace("\n", "\r\n\r\n") + .Replace("
    ", "\r\n\r\n") + ; + md += "\n\n"; + return md; + } + + public static void Dump(this Markdig.Syntax.ParagraphBlock block) + { + //if (block.Lines != null) + //{ + // foreach (var line in block.Lines) + // { + // var stringline = line as Markdig.Helpers.StringLine?; + // Debug.WriteLine(stringline.ToString()); + // } + //} + } + public static void Dump(this Markdig.Syntax.ListBlock block) + { + Debug.WriteLine(block.BulletType); + foreach (var inblock in block) + { + inblock.Dump(); + } + } + public static void Dump(Markdig.Syntax.ListItemBlock block) + { + foreach (var inblock in block) + { + inblock.Dump(); + } + } + public static void Dump(this Markdig.Syntax.HeadingBlock block) + { + Debug.WriteLine(block.HeaderChar); + Debug.WriteLine(block.Level); + //foreach(var line in block.Lines.Lines) + //{ + // DumpStringLine(line); + //} + } + public static void Dump(this Markdig.Helpers.StringLine line) + { + Console.WriteLine(line.ToString()); + } + public static void Dump(this Markdig.Syntax.Block block) + { + Debug.WriteLine(block.Column); + Debug.WriteLine(block.IsBreakable); + Debug.WriteLine(block.IsOpen); + Debug.WriteLine(block.Line); + Debug.WriteLine(block.RemoveAfterProcessInlines); + Debug.WriteLine(block.Span.ToString()); + //Debug.WriteLine(block.Span.ToString(MD)); + Debug.WriteLine(block.ToString()); + if (block is Markdig.Syntax.ParagraphBlock) + { + (block as Markdig.Syntax.ParagraphBlock).Dump(); + } + if (block is Markdig.Syntax.ListBlock) + { + (block as Markdig.Syntax.ListBlock).Dump(); + } + if (block is Markdig.Syntax.HeadingBlock) + { + (block as Markdig.Syntax.HeadingBlock).Dump(); + } + if (block is Markdig.Syntax.ListItemBlock) + { + (block as Markdig.Syntax.ListItemBlock).Dump(); + } + } + public static void Dump(this Markdig.Syntax.MarkdownDocument document) + { + foreach (var block in document) + { + block.Dump(); + } + } + + + } +} diff --git a/AideDeJeu/AideDeJeuCmd/Program.cs b/AideDeJeu/AideDeJeuCmd/Program.cs index 1eab4750..d6103054 100644 --- a/AideDeJeu/AideDeJeuCmd/Program.cs +++ b/AideDeJeu/AideDeJeuCmd/Program.cs @@ -1,8 +1,10 @@ using AideDeJeu.Services; +using AideDeJeu.Tools; using AideDeJeuLib.Monsters; using AideDeJeuLib.Spells; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -15,329 +17,52 @@ using Xamarin.Forms.Internals; namespace AideDeJeuCmd { - public static class MarkdownExtensions - { - public static string ToString(this Markdig.Syntax.SourceSpan span, string md) - { - return md.Substring(span.Start, span.Length); - } - public static string ToContainerString(this Markdig.Syntax.Inlines.ContainerInline inlines) - { - var str = string.Empty; - foreach(var inline in inlines) - { - Console.WriteLine(inline.GetType()); - string add = string.Empty; - if (inline is Markdig.Syntax.Inlines.LineBreakInline) - { - add = "\n"; - } - else if (inline is Markdig.Syntax.Inlines.LiteralInline) - { - var literalInline = inline as Markdig.Syntax.Inlines.LiteralInline; - add = literalInline.Content.ToString(); - } - else if (inline is Markdig.Syntax.Inlines.EmphasisInline) - { - var emphasisInline = inline as Markdig.Syntax.Inlines.EmphasisInline; - var delimiterChar = emphasisInline.DelimiterChar.ToString(); - if (emphasisInline.IsDouble) - { - delimiterChar += delimiterChar; - } - add = delimiterChar + emphasisInline.ToContainerString() + delimiterChar; - } - else if (inline is Markdig.Syntax.Inlines.ContainerInline) - { - var containerInline = inline as Markdig.Syntax.Inlines.ContainerInline; - add = containerInline.ToContainerString(); - } - else - { - add = inline.ToString(); - } - Console.WriteLine(add); - str += add; - } - return str; - } - public static string ToParagraphString(this Markdig.Syntax.ParagraphBlock paragraphBlock) - { - var str = string.Empty; - str += paragraphBlock.Inline.ToContainerString(); - if (paragraphBlock.IsBreakable) - { - str += "\n"; - } - return str; - } - - public static string ToMarkdownString(this IEnumerable spells) - { - var md = string.Empty; - foreach(var spell in spells) - { - md += spell.ToMarkdownString(); - } - return md; - } - public static string ToMarkdownString(this Spell spell) - { - var md = string.Empty; - md += string.Format("# {0}\n", spell.NamePHB); - md += string.Format("- NameVO: {0}\n", spell.NameVO); - md += string.Format("- CastingTime: {0}\n", spell.CastingTime); - md += string.Format("- Components: {0}\n", spell.Components); - md += string.Format("- Duration: {0}\n", spell.Duration); - md += string.Format("- LevelType: {0}\n", spell.LevelType); - md += string.Format("- Range: {0}\n", spell.Range); - var regex = new Regex("(?\\(.*\\)) (?.*)"); - var match = regex.Match(spell.Source); - var source = match.Groups["source"].Value; - var classes = match.Groups["classes"].Value; - md += string.Format("- Source: {0}\n", source); - md += string.Format("- Classes: {0}\n", classes.Replace(" ;", ",").Trim().Trim(',')); - md += "\n"; - md += "### Description\n\n"; - md += spell - .DescriptionHtml - .Replace("", "**") - .Replace("", "**") - .Replace("", "_") - .Replace("", "_") - .Replace("
  • ", "* ") - .Replace("
  • ", "") - .Replace("\n", "\r\n\r\n") - .Replace("
    ", "\r\n\r\n") - ; - md += "\n\n"; - return md; - } - } class Program { - public class MarkdownConverter - { - public IEnumerable MarkdownToSpells(string md) - { - var spells = new List(); - var document = Markdig.Parsers.MarkdownParser.Parse(md); - Spell spell = null; - foreach (var block in document) - { - //DumpBlock(block); - if (block is Markdig.Syntax.HeadingBlock) - { - var headingBlock = block as Markdig.Syntax.HeadingBlock; - //DumpHeadingBlock(headingBlock); - if (headingBlock.HeaderChar == '#' && headingBlock.Level == 1) - { - if (spell != null) - { - spells.Add(spell); - } - spell = new Spell(); - spell.Name = spell.NamePHB = headingBlock.Inline.ToContainerString(); - //Console.WriteLine(spell.Name); - } - } - if (block is Markdig.Syntax.ParagraphBlock) - { - var paragraphBlock = block as Markdig.Syntax.ParagraphBlock; - spell.DescriptionHtml += paragraphBlock.ToParagraphString(); - ////DumpParagraphBlock(paragraphBlock); - //Console.WriteLine(paragraphBlock.IsBreakable); - //spell.DescriptionHtml += paragraphBlock.Inline.ToContainerString(); - //if(paragraphBlock.IsBreakable) - //{ - // spell.DescriptionHtml += "\n"; - //} - } - if (block is Markdig.Syntax.ListBlock) - { - var listBlock = block as Markdig.Syntax.ListBlock; - //DumpListBlock(listBlock); - if (listBlock.BulletType == '-') - { - spell.Source = ""; - foreach (var inblock in listBlock) - { - //DumpBlock(inblock); - var regex = new Regex("(?.*?): (?.*)"); - if (inblock is Markdig.Syntax.ListItemBlock) - { - var listItemBlock = inblock as Markdig.Syntax.ListItemBlock; - foreach (var ininblock in listItemBlock) - { - //DumpBlock(ininblock); - if (ininblock is Markdig.Syntax.ParagraphBlock) - { - var paragraphBlock = ininblock as Markdig.Syntax.ParagraphBlock; - //DumpParagraphBlock(paragraphBlock); - var str = paragraphBlock.Inline.ToContainerString(); - var match = regex.Match(str); - var key = match.Groups["key"].Value; - var value = match.Groups["value"].Value; - switch (key) - { - case "NameVO": - spell.NameVO = value; - break; - case "CastingTime": - spell.CastingTime = value; - break; - case "Components": - spell.Components = value; - break; - case "Duration": - spell.Duration = value; - break; - case "LevelType": - spell.LevelType = value; - break; - case "Range": - spell.Range = value; - break; - case "Source": - spell.Source += value + " "; - break; - case "Classes": - spell.Source += value; - break; - } - } - } - //DumpListItemBlock(inblock as Markdig.Syntax.ListItemBlock); - } - } - } - else - { - foreach (var inblock in listBlock) - { - if (inblock is Markdig.Syntax.ListItemBlock) - { - var listItemBlock = inblock as Markdig.Syntax.ListItemBlock; - foreach (var ininblock in listItemBlock) - { - //DumpBlock(ininblock); - if (ininblock is Markdig.Syntax.ParagraphBlock) - { - var paragraphBlock = ininblock as Markdig.Syntax.ParagraphBlock; - spell.DescriptionHtml += listBlock.BulletType + " " + paragraphBlock.ToParagraphString(); - } - } - } - } - } - } - - } - if (spell != null) - { - spells.Add(spell); - } - return spells; - } - } - - static void DumpParagraphBlock(Markdig.Syntax.ParagraphBlock block) - { - //if (block.Lines != null) - //{ - // foreach (var line in block.Lines) - // { - // var stringline = line as Markdig.Helpers.StringLine?; - // Console.WriteLine(stringline.ToString()); - // } - //} - } - static void DumpListBlock(Markdig.Syntax.ListBlock block) - { - Console.WriteLine(block.BulletType); - foreach(var inblock in block) - { - DumpBlock(inblock); - } - } - static void DumpListItemBlock(Markdig.Syntax.ListItemBlock block) - { - foreach(var inblock in block) - { - DumpBlock(inblock); - } - } - static void DumpHeadingBlock(Markdig.Syntax.HeadingBlock block) - { - Console.WriteLine(block.HeaderChar); - Console.WriteLine(block.Level); - //foreach(var line in block.Lines.Lines) - //{ - // DumpStringLine(line); - //} - } - static void DumpStringLine(Markdig.Helpers.StringLine line) - { - Console.WriteLine(line.ToString()); - } - static void DumpBlock(Markdig.Syntax.Block block) - { - Console.WriteLine(block.Column); - Console.WriteLine(block.IsBreakable); - Console.WriteLine(block.IsOpen); - Console.WriteLine(block.Line); - Console.WriteLine(block.RemoveAfterProcessInlines); - Console.WriteLine(block.Span.ToString()); - //Console.WriteLine(block.Span.ToString(MD)); - Console.WriteLine(block.ToString()); - if(block is Markdig.Syntax.ParagraphBlock) - { - DumpParagraphBlock(block as Markdig.Syntax.ParagraphBlock); - } - if(block is Markdig.Syntax.ListBlock) - { - DumpListBlock(block as Markdig.Syntax.ListBlock); - } - if (block is Markdig.Syntax.HeadingBlock) - { - DumpHeadingBlock(block as Markdig.Syntax.HeadingBlock); - } - if (block is Markdig.Syntax.ListItemBlock) - { - DumpListItemBlock(block as Markdig.Syntax.ListItemBlock); - } - } - static void DumpMarkdownDocument(Markdig.Syntax.MarkdownDocument document) - { - foreach (var block in document) - { - DumpBlock(block); - } - } - - static async Task> TestMarkdown(string filename) + static async Task> TestMarkdown(string filename) { using (var sr = new StreamReader(filename)) { var md = await sr.ReadToEndAsync(); var document = Markdig.Parsers.MarkdownParser.Parse(md); //DumpMarkdownDocument(document); - var converter = new MarkdownConverter(); - var spellss = converter.MarkdownToSpells(md); + + var spellss = document.ToSpells(); Console.WriteLine("ok"); var md2 = spellss.ToMarkdownString(); if(md.CompareTo(md2) != 0) { - Console.WriteLine("failed"); + Debug.WriteLine("failed"); } return spellss; } } + static async Task> TestMarkdownMonsters(string filename) + { + using (var sr = new StreamReader(filename)) + { + var md = await sr.ReadToEndAsync(); + var document = Markdig.Parsers.MarkdownParser.Parse(md); + //DumpMarkdownDocument(document); + + var monsters = document.ToMonsters(); + document.Dump(); + Console.WriteLine("ok"); + //var md2 = monsters.ToMarkdownString(); + //if (md.CompareTo(md2) != 0) + //{ + // Debug.WriteLine("failed"); + //} + return monsters; + } + } + static async Task Main(string[] args) { - var spellss = await TestMarkdown(@"..\..\..\..\..\Data\spells_hd.md"); + //var spellss = await TestMarkdown(@"..\..\..\..\..\Data\spells_hd.md"); + var monsterss = await TestMarkdownMonsters(@"..\..\..\..\..\Data\monsters_hd.md"); return; string dataDir = @"..\..\..\..\..\Data\"; //string ignoreDir = @"..\..\..\..\..\Ignore\"; diff --git a/Data/monsters_hd.md b/Data/monsters_hd.md index 9c89926d..9f5f3f69 100644 --- a/Data/monsters_hd.md +++ b/Data/monsters_hd.md @@ -6,40 +6,40 @@ |FOR|DEX|CON|INT|SAG|CHA| |21 (+5)|9 (-1)|15 (+2)|18 (+4)|15 (+2)|18 (+4)| - SavingThrows: Con +6, Int +8, Sag +6 -- Competencies: Histoire +12, Perception +10 +- Skills: Histoire +12, Perception +10 - Senses: vision dans le noir 36 m, Perception passive 20 -- Languages: profond, télépathie 36 m +- Languages: profond, télépathie 36 m - Challenge: 10 (5 900 PX) -## Capacités +## Capacités -**_Amphibie._** L'aboleth peut respirer à l'air libre et sous l'eau. +**_Amphibie._** L'aboleth peut respirer à l'air libre et sous l'eau. -**_Nuage muqueux._** Sous l'eau, l'aboleth est entouré de mucosités aux propriétés transformatives. Les créatures qui touchent l'aboleth ou qui réussissent une attaque au corps à corps contre lui à une distance maximale de 1,50 mètre doivent effectuer un jet de sauvegarde de Constitution DD 14. En cas d'échec, la créature est malade pendant 1d4 heures. Une créature victime de cette maladie peut respirer uniquement sous l'eau. +**_Nuage muqueux._** Sous l'eau, l'aboleth est entouré de mucosités aux propriétés transformatives. Les créatures qui touchent l'aboleth ou qui réussissent une attaque au corps à corps contre lui à une distance maximale de 1,50 mètre doivent effectuer un jet de sauvegarde de Constitution DD 14. En cas d'échec, la créature est malade pendant 1d4 heures. Une créature victime de cette maladie peut respirer uniquement sous l'eau. -**_Télépathie inquisitrice._** Si une créature communique par télépathie avec l'aboleth, celui-ci prend connaissance des plus grands désirs de la créature, à condition qu'elle soit dans son champ de vision. +**_Télépathie inquisitrice._** Si une créature communique par télépathie avec l'aboleth, celui-ci prend connaissance des plus grands désirs de la créature, à condition qu'elle soit dans son champ de vision. ## Actions -**_Asservir (3/jour)._** L'aboleth cible une créature située à 9 mètres ou moins et dans son champ de vision. La cible doit réussir un jet de sauvegarde de Sagesse DD 14 pour ne pas être magiquement charmée par l'aboleth. Le charme disparaît quand l'aboleth meurt ou s'il se trouve sur un plan d'existence différent de celui de la cible. La cible charmée est sous le contrôle de l'aboleth et ne peut entreprendre aucune réaction. L'aboleth et la cible peuvent communiquer par télépathie, quelle que soit la distance qui les sépare. Chaque fois que la cible charmée subit des dégâts, elle peut retenter le jet de sauvegarde. En cas de réussite, l'effet prend fn. La cible peut retenter le jet de sauvegarde dès que l'aboleth s'éloigne de plus de 1,5 kilomètre d'elle, mais pas plus d'une fois toutes les 24 heures. +**_Asservir (3/jour)._** L'aboleth cible une créature située à 9 mètres ou moins et dans son champ de vision. La cible doit réussir un jet de sauvegarde de Sagesse DD 14 pour ne pas être magiquement charmée par l'aboleth. Le charme disparaît quand l'aboleth meurt ou s'il se trouve sur un plan d'existence différent de celui de la cible. La cible charmée est sous le contrôle de l'aboleth et ne peut entreprendre aucune réaction. L'aboleth et la cible peuvent communiquer par télépathie, quelle que soit la distance qui les sépare. Chaque fois que la cible charmée subit des dégâts, elle peut retenter le jet de sauvegarde. En cas de réussite, l'effet prend fn. La cible peut retenter le jet de sauvegarde dès que l'aboleth s'éloigne de plus de 1,5 kilomètre d'elle, mais pas plus d'une fois toutes les 24 heures. **_Attaques multiples._** L'aboleth effectue trois attaques de tentacule. -**_Queue._** Attaque d'arme au corps à corps : +9 pour toucher, allonge 3 m, une cible. -Touché : 15 (3d6+5) dégâts contondants. +**_Queue._** Attaque d'arme au corps à corps : +9 pour toucher, allonge 3 m, une cible. +Touché : 15 (3d6+5) dégâts contondants. -**_Tentacule._** Attaque d'arme au corps à corps : +9 pour toucher, allonge 3 m, une cible. -Touché : 12 (2d6+5) dégâts contondants. Si la cible est une créature, elle doit réussir un jet de sauvegarde de Constitution DD 14 pour ne pas tomber malade. La maladie ne produit aucun effet pendant 1 minute et peut être soignée par tout effet magique qui soigne les maladies. Au bout d'une minute, la peau de la créature malade devient translucide et visqueuse, la victime ne peut récupérer de points de vie que lorsqu'elle est sous l'eau et la maladie ne peut être soignée qu'avec une guérison ou un autre sort de niveau 6 ou plus qui soigne les maladies. Quand la créature n'est pas dans l'eau, elle subit 6 (1d12) dé- gâts d'acide toutes les 10 minutes, à moins que sa peau ne soit humidifée toutes les dix minutes. +**_Tentacule._** Attaque d'arme au corps à corps : +9 pour toucher, allonge 3 m, une cible. +Touché : 12 (2d6+5) dégâts contondants. Si la cible est une créature, elle doit réussir un jet de sauvegarde de Constitution DD 14 pour ne pas tomber malade. La maladie ne produit aucun effet pendant 1 minute et peut être soignée par tout effet magique qui soigne les maladies. Au bout d'une minute, la peau de la créature malade devient translucide et visqueuse, la victime ne peut récupérer de points de vie que lorsqu'elle est sous l'eau et la maladie ne peut être soignée qu'avec une guérison ou un autre sort de niveau 6 ou plus qui soigne les maladies. Quand la créature n'est pas dans l'eau, elle subit 6 (1d12) dé- gâts d'acide toutes les 10 minutes, à moins que sa peau ne soit humidifée toutes les dix minutes. -## Actions légendaires +## Actions légendaires -L'aboleth peut effectuer 3 actions légendaires qu'il choisit parmi celles décrites ici. Il ne peut en choisir qu'une seule à la fois et uniquement à la fn du tour d'une autre créature. L'aboleth récupère au début de son tour l'utilisation des actions légendaires déjà effectuées. +L'aboleth peut effectuer 3 actions légendaires qu'il choisit parmi celles décrites ici. Il ne peut en choisir qu'une seule à la fois et uniquement à la fn du tour d'une autre créature. L'aboleth récupère au début de son tour l'utilisation des actions légendaires déjà effectuées. **_Balayage de la queue._** L'aboleth effectue une attaque de queue. -**_Détecter._** L'aboleth effectue un test de Sagesse (Perception). +**_Détecter._** L'aboleth effectue un test de Sagesse (Perception). -**_Succion psychique (coûte 2 actions)._** Une créature charmée par l'aboleth subit 10 (3d6) dégâts psychiques et l'aboleth récupère un nombre de points de vie égal aux dégâts subis par la créature. +**_Succion psychique (coûte 2 actions)._** Une créature charmée par l'aboleth subit 10 (3d6) dégâts psychiques et l'aboleth récupère un nombre de points de vie égal aux dégâts subis par la créature. # Abothid @@ -50,32 +50,32 @@ L'aboleth peut effectuer 3 actions l |FOR|DEX|CON|INT|SAG|CHA| |16 (+3)|14 (+2)|15 (+2)|18 (+4)|13 (+1)|14 (+2)| - SavingThrows: Int +7, Sag +4, Cha +5 -- Competencies: Intimidation +7, Perception +6, Perspicacité +6 +- Competencies: Intimidation +7, Perception +6, Perspicacité +6 - DamageResistancies: acide et froid - DamageVulneraibilities: feu - Immunity: poison -- Senses: vision dans le noir (18 m) ; Perception passive 16 -- Languages: Commun des profondeurs, profond, télépathie (18 m), langue raciale de la créature d'origine +- Skills: vision dans le noir (18 m) ; Perception passive 16 +- Languages: Commun des profondeurs, profond, télépathie (18 m), langue raciale de la créature d'origine - Challenge: 7 (2 900 XP) -## Capacités +## Capacités -**_Nuage empoisonné._** L'abothid est constamment entouré d'un nuage de gaz toxique pour les non-abothids. Toute créature située à 1,50 mètres ou moins doit réussir un jet de sauvegarde de Constitution (DD 14) sous peine de su?oquer et être étourdie pendant 1 minute. +**_Nuage empoisonné._** L'abothid est constamment entouré d'un nuage de gaz toxique pour les non-abothids. Toute créature située à 1,50 mètres ou moins doit réussir un jet de sauvegarde de Constitution (DD 14) sous peine de su?oquer et être étourdie pendant 1 minute. ## Actions -**_Attaques multiples._** L'abothid e?ectue deux attaques de tentacules. S'il empoigne une créature, l'abothid peut également utiliser une fois le dard de sa langue. +**_Attaques multiples._** L'abothid e?ectue deux attaques de tentacules. S'il empoigne une créature, l'abothid peut également utiliser une fois le dard de sa langue. -**_Tentacule._** Attaque d'arme au corps à corps : +8 pour toucher, allonge 1,50 m, une cible. -Touché : 11 (2d8+3) dégâts contondants (constriction). -Si la cible est une créature de taille M ou plus petite, elle est empoignée (évasion DD 14). L'abothid a deux tentacules dont chacun peut empoigner une cible. +**_Tentacule._** Attaque d'arme au corps à corps : +8 pour toucher, allonge 1,50 m, une cible. +Touché : 11 (2d8+3) dégâts contondants (constriction). +Si la cible est une créature de taille M ou plus petite, elle est empoignée (évasion DD 14). L'abothid a deux tentacules dont chacun peut empoigner une cible. -**_Dard de langue._** Attaque d'arme au corps à corps (basée sur la Dextérité) : +7 pour toucher, allonge 1,50 m, une cible. Touché : 6 (1d6+2) dégâts perçants. -Si la cible est une créature humanoïde, elle doit réussir un jet de sauvegarde de Constitution (DD 14) sous peine d'être infectée par une maladie : l'injection d'un minuscule œuf d'abothid. -Un humanoïde ne peut mener qu'un seul œuf d'abothid à son terme. L'éclosion de l'œuf se fait après 24 heures et donne naissance à une larve qui remonte jusqu'au cerveau en un nombre d'heures égal à 1d12 + valeur de Constitution de la cible infectée. Durant cette période, la victime est empoisonnée et ne peut plus regagner de points de vie. Une fois que la larve a atteint le cerveau, la cible infectée voit son maximum de points de vie se réduire de 10 (3d6) points par 24 heures. Si la larve réduit le maximum des points de vie de la cible à 0, cela signife que sa transformation en abothid est achevée. Seul un sort de souhait peut inverser la transformation. +**_Dard de langue._** Attaque d'arme au corps à corps (basée sur la Dextérité) : +7 pour toucher, allonge 1,50 m, une cible. Touché : 6 (1d6+2) dégâts perçants. +Si la cible est une créature humanoïde, elle doit réussir un jet de sauvegarde de Constitution (DD 14) sous peine d'être infectée par une maladie : l'injection d'un minuscule Å“uf d'abothid. +Un humanoïde ne peut mener qu'un seul Å“uf d'abothid à son terme. L'éclosion de l'Å“uf se fait après 24 heures et donne naissance à une larve qui remonte jusqu'au cerveau en un nombre d'heures égal à 1d12 + valeur de Constitution de la cible infectée. Durant cette période, la victime est empoisonnée et ne peut plus regagner de points de vie. Une fois que la larve a atteint le cerveau, la cible infectée voit son maximum de points de vie se réduire de 10 (3d6) points par 24 heures. Si la larve réduit le maximum des points de vie de la cible à 0, cela signife que sa transformation en abothid est achevée. Seul un sort de souhait peut inverser la transformation. -**_Asservissement (1/jour)._** L'abothid cible une créature dans son champ de vision située à 18 mètres ou moins. La cible doit réussir un jet de sauvegarde de Sagesse (DD 14) sous peine d'être charmée magiquement par l'abothid pour une durée de 1 heure. -La cible charmée est sous le contrôle de l'abothid et ne peut entreprendre aucune action mis à part se défendre au mieux de ses capacités. L'abothid peut utiliser sa télépathie pour donner des ordres à la cible. -Chaque fois que la cible charmée subit des dégâts, elle peut retenter le jet de sauvegarde. En cas de réussite, l'e?et prend fn. L'e?et peut prendre fn avant si l'abothid est détruit, s'il s'éloigne de plus de 1,5 kilomètre de la cible, s'il se trouve sur un plan d'existence di?érent de celui de la cible ou, enfn, s'il e?ectue une action bonus pour mettre fn à cet e?et. +**_Asservissement (1/jour)._** L'abothid cible une créature dans son champ de vision située à 18 mètres ou moins. La cible doit réussir un jet de sauvegarde de Sagesse (DD 14) sous peine d'être charmée magiquement par l'abothid pour une durée de 1 heure. +La cible charmée est sous le contrôle de l'abothid et ne peut entreprendre aucune action mis à part se défendre au mieux de ses capacités. L'abothid peut utiliser sa télépathie pour donner des ordres à la cible. +Chaque fois que la cible charmée subit des dégâts, elle peut retenter le jet de sauvegarde. En cas de réussite, l'e?et prend fn. L'e?et peut prendre fn avant si l'abothid est détruit, s'il s'éloigne de plus de 1,5 kilomètre de la cible, s'il se trouve sur un plan d'existence di?érent de celui de la cible ou, enfn, s'il e?ectue une action bonus pour mettre fn à cet e?et. -**_Absorption psychique._** L'abothid peut se nourrir du psychisme d'une cible asservie située à 18 mètres ou moins pour récupérer des points de vie. Il occasionne ainsi 10 (3d6) points de dégâts psychiques à sa cible et récupère un nombre de points de vie équivalent. Cette aptitude utilise toutes les actions de l'abothid à ce tour. +**_Absorption psychique._** L'abothid peut se nourrir du psychisme d'une cible asservie située à 18 mètres ou moins pour récupérer des points de vie. Il occasionne ainsi 10 (3d6) points de dégâts psychiques à sa cible et récupère un nombre de points de vie équivalent. Cette aptitude utilise toutes les actions de l'abothid à ce tour.