Fix links to AoN
This commit is contained in:
@@ -46,7 +46,7 @@ pub struct Creature {
|
||||
pub legacy_id: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
fn fix_action_icons(input: String) -> String {
|
||||
fn fix_action_icons(input: &str) -> String {
|
||||
input
|
||||
.replace(r#"<actions string="Free Action" />"#, "`[free-action]`")
|
||||
.replace(r#"<actions string="Single Action" />"#, "`[one-action]`")
|
||||
@@ -55,10 +55,30 @@ fn fix_action_icons(input: String) -> String {
|
||||
.replace(r#"<actions string="Reaction" />"#, "`[reaction]`")
|
||||
}
|
||||
|
||||
fn fix_html(s: String) -> String {
|
||||
/// Replace relative links with absolute links to Archives of Nethys (<https://2e.aonprd.com/>).
|
||||
fn fix_links(s: &str) -> String {
|
||||
let markdown_link_ro = Regex::new(
|
||||
r#"(?x)
|
||||
\[
|
||||
(?P<title>[^\[\]]*)
|
||||
\]
|
||||
\(
|
||||
(?P<link>[^\(\)]*)
|
||||
\)
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
markdown_link_ro
|
||||
.replace_all(&s, "[$title](https://2e.aonprd.com$link)")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn fix_html(s: &str) -> String {
|
||||
let html_tags_re = Regex::new(r"<[^<>]*>").unwrap();
|
||||
|
||||
let s = fix_action_icons(s);
|
||||
let s = fix_links(&s);
|
||||
let s = s.replace("<br />", "\n");
|
||||
let s = html_tags_re.replace_all(&s, "");
|
||||
|
||||
@@ -66,7 +86,7 @@ fn fix_html(s: String) -> String {
|
||||
}
|
||||
|
||||
fn split_sections(s: &str) -> Vec<String> {
|
||||
let s = fix_html(s.to_string());
|
||||
let s = fix_html(s);
|
||||
let re_split = Regex::new(r"\r?\n\s*\r?\n").unwrap();
|
||||
re_split.split(&s).map(String::from).collect()
|
||||
}
|
||||
@@ -88,7 +108,7 @@ fn parse_ability_section(
|
||||
|
||||
let processed: Vec<String> = relevant_sections
|
||||
.map(|s| s.replace("\r", ""))
|
||||
.map(|s| fix_action_icons(s.to_string()))
|
||||
.map(|s| fix_action_icons(&s))
|
||||
.map(|s| s.replace('\n', " "))
|
||||
.collect();
|
||||
|
||||
@@ -117,7 +137,7 @@ fn parse_defensive_abilities(markdown: &str) -> String {
|
||||
|
||||
let processed: Vec<String> = relevant_sections
|
||||
.map(|s| s.replace("\r", ""))
|
||||
.map(|s| fix_action_icons(s.to_string()))
|
||||
.map(|s| fix_action_icons(&s))
|
||||
.map(|s| s.replace('\n', " "))
|
||||
.collect();
|
||||
|
||||
@@ -128,7 +148,7 @@ fn parse_general_abilities(markdown: &str) -> String {
|
||||
parse_ability_section(
|
||||
markdown,
|
||||
|s| s.starts_with("**Cha**"),
|
||||
|s| s.starts_with("**Fort**"),
|
||||
|s| s.starts_with("**AC**"),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -166,7 +186,7 @@ pub fn creature_to_obsidian(sb: &Creature) -> String {
|
||||
]
|
||||
.into_iter()
|
||||
.filter_map(|(prefix, list)| Some(prefix).zip(list.as_deref()))
|
||||
.map(|(prefix, list)| format!("**{prefix}** {list}"))
|
||||
.map(|(prefix, list)| format!("**{prefix}** {}", fix_html(list)))
|
||||
.fold(hp_text, |b, s| b + "; " + &s);
|
||||
|
||||
let general_abilities = parse_general_abilities(&sb.markdown);
|
||||
@@ -184,12 +204,12 @@ pub fn creature_to_obsidian(sb: &Creature) -> String {
|
||||
&mut s,
|
||||
"\n**Perception** {}; {}\n",
|
||||
plussed(sb.perception),
|
||||
&sb.sense_markdown.as_deref().unwrap_or(""), // TODO
|
||||
fix_html(&sb.sense_markdown.as_deref().unwrap_or("")), // TODO
|
||||
);
|
||||
_ = writeln!(
|
||||
&mut s,
|
||||
"\n**Skills**: {}\n",
|
||||
sb.skill_markdown.as_deref().unwrap_or_default() // TODO
|
||||
fix_html(sb.skill_markdown.as_deref().unwrap_or_default()) // TODO
|
||||
);
|
||||
_ = writeln!(
|
||||
&mut s,
|
||||
@@ -219,10 +239,41 @@ pub fn creature_to_obsidian(sb: &Creature) -> String {
|
||||
_ = writeln!(
|
||||
&mut s,
|
||||
"\n**Speed**: {}\n",
|
||||
&sb.speed_markdown.as_deref().unwrap_or_default() // TODO
|
||||
fix_html(&sb.speed_markdown.as_deref().unwrap_or_default()) // TODO
|
||||
);
|
||||
_ = writeln!(&mut s, "{}", offensive_abilities);
|
||||
_ = writeln!(&mut s, "\n```");
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::creature::{Creature, creature_to_obsidian};
|
||||
|
||||
const EXAMPLE_CREATURE_1: &str = include_str!("creature1.example.json");
|
||||
const EXAMPLE_CREATURE_2: &str = include_str!("creature2.example.json");
|
||||
|
||||
#[test]
|
||||
fn fix_link() {
|
||||
let s = r#"blah blah [Foo Bar](/Monsters.aspx?ID=2871) blah blah
|
||||
new line
|
||||
[More Links](/Monsters.aspx?ID=2871)
|
||||
"#;
|
||||
insta::assert_snapshot!(&super::fix_links(s));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_creature_1() {
|
||||
let creature: Creature = serde_json::from_str(EXAMPLE_CREATURE_1).unwrap();
|
||||
let obsidian = creature_to_obsidian(&creature);
|
||||
insta::assert_snapshot!(&obsidian);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_creature_2() {
|
||||
let creature: Creature = serde_json::from_str(EXAMPLE_CREATURE_2).unwrap();
|
||||
let obsidian = creature_to_obsidian(&creature);
|
||||
insta::assert_snapshot!(&obsidian);
|
||||
}
|
||||
}
|
||||
|
||||
133
src/creature1.example.json
Normal file
133
src/creature1.example.json
Normal file
File diff suppressed because one or more lines are too long
111
src/creature2.example.json
Normal file
111
src/creature2.example.json
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"ac": 12,
|
||||
"ac_scale_number": 3,
|
||||
"attack_bonus": [
|
||||
7
|
||||
],
|
||||
"attack_bonus_scale_number": [
|
||||
3
|
||||
],
|
||||
"category": "creature",
|
||||
"charisma": -2,
|
||||
"charisma_scale_number": 2,
|
||||
"constitution": 2,
|
||||
"constitution_scale_number": 3,
|
||||
"creature_ability": [
|
||||
"Slow",
|
||||
"Zombie Bite"
|
||||
],
|
||||
"creature_family": "Zombie",
|
||||
"creature_family_markdown": "[Zombie](/MonsterFamilies.aspx?ID=487)",
|
||||
"dexterity": -2,
|
||||
"dexterity_scale_number": 2,
|
||||
"fortitude_save": 6,
|
||||
"fortitude_save_scale_number": 3,
|
||||
"hp_raw": "20 ( void healing )",
|
||||
"hp_scale_number": 4,
|
||||
"id": "creature-3249",
|
||||
"image": [
|
||||
"/Images/Monsters/Zombie_Shambler.webp"
|
||||
],
|
||||
"immunity_markdown": "bleed, [death](/Traits.aspx?ID=571) effects, [disease](/Traits.aspx?ID=578), [mental](/Traits.aspx?ID=647), [paralyzed](/Conditions.aspx?ID=85), [poison](/Traits.aspx?ID=669), [unconscious](/Conditions.aspx?ID=95)",
|
||||
"intelligence": -5,
|
||||
"intelligence_scale_number": 2,
|
||||
"language_markdown": "",
|
||||
"legacy_id": [
|
||||
"creature-423"
|
||||
],
|
||||
"level": -1,
|
||||
"markdown": "<title level=\"1\" pfs=\"Standard\">[Zombie Shambler](/Monsters.aspx?ID=3249)</title>\r\n\r\n<row gap=\"medium\">\r\n\r\n<column gap=\"medium\" flex=\"1 1 400px\">\r\n\r\nA zombie shambler is a slow-moving horror dangerous in larger groups.\r\n\r\n<column gap=\"tiny\">\r\n**[Recall Knowledge - Undead](/Rules.aspx?ID=563)**\r\n([Religion](/Skills.aspx?ID=46)): DC 13\r\n\r\n**[Unspecific Lore](/Rules.aspx?ID=563)**: DC 11\r\n\r\n**[Specific Lore](/Rules.aspx?ID=563)**: DC 8\r\n</column>\r\n\r\n</column>\r\n\r\n<column gap=\"medium\">\r\n<image src=\"/images/Monsters/Zombie_Shambler.webp\" />\r\n</column>\r\n\r\n</row>\r\n\r\n<title level=\"2\" right=\"Creature -1\">[Zombie Shambler](/Monsters.aspx?ID=3249)</title>\r\n\r\n<traits>\r\n<trait label=\"Medium\" />\r\n<trait label=\"Mindless\" url=\"/Traits.aspx?ID=652\" />\r\n<trait label=\"Undead\" url=\"/Traits.aspx?ID=722\" />\r\n<trait label=\"Unholy\" url=\"/Traits.aspx?ID=521\" />\r\n<trait label=\"Zombie\" url=\"/Traits.aspx?ID=782\" />\r\n</traits>\r\n\r\n<column gap=\"tiny\">\r\n\r\n**Source** [Monster Core](/Sources.aspx?ID=221) pg. 356\n\n**Perception** +0; [darkvision](/MonsterAbilities.aspx?ID=59)\r\n\r\n**Languages**\n\n**Skills**\r\n[Athletics](/Skills.aspx?ID=3) +7\r\n\r\n<row gap=\"medium\">\r\n**Str** +3\r\n\r\n**Dex** -2\r\n\r\n**Con** +2\r\n\r\n**Int** -5\r\n\r\n**Wis** +0\r\n\r\n**Cha** -2\r\n</row>\r\n\r\n**Slow** A zombie is permanently [slowed 1](/Conditions.aspx?ID=92) and can't use reactions.\n\n</column>\r\n\r\n---\r\n\r\n<column gap=\"tiny\">\r\n\r\n<row gap=\"medium\">\r\n**AC** 12 \r\n\r\n**Fort** +6 \r\n\r\n**Ref** +0 \r\n\r\n**Will** +2 \n\n</row>\r\n\r\n<row gap=\"medium\">\r\n**HP** 20\r\n([void healing](/MonsterAbilities.aspx?ID=83))\n\n</row>\r\n\r\n**Immunities**\r\nbleed, [death](/Traits.aspx?ID=571) effects, [disease](/Traits.aspx?ID=578), [mental](/Traits.aspx?ID=647), [paralyzed](/Conditions.aspx?ID=85), [poison](/Traits.aspx?ID=669), [unconscious](/Conditions.aspx?ID=95)\n\n**Weaknesses**\r\nslashing 5, vitality 5\n\n</column>\r\n\r\n---\r\n\r\n<column gap=\"tiny\">\r\n\r\n**Speed** 25 feet\r\n\r\n**Melee**\r\n<actions string=\"Single Action\" />\r\nfist +7,\r\n**Damage** 1d6+3 bludgeoning plus [Grab](/MonsterAbilities.aspx?ID=45)\n\n**Zombie Bite** <actions string=\"Single Action\" /> **Requirements** The zombie has a creature **grabbed** or [restrained](/Conditions.aspx?ID=90); **Effect** The zombie makes a jaws [unarmed](/Traits.aspx?ID=719) melee Strike against that creature with an attack modifier of +7 that deals 1d8+3 piercing damage.\r\n\r\n</column>\n\n<document level=\"2\" id=\"creature-family-487\" />",
|
||||
"name": "Zombie Shambler",
|
||||
"perception": 0,
|
||||
"perception_scale_number": 1,
|
||||
"pfs": "Standard",
|
||||
"primary_source_category": "Rulebooks",
|
||||
"rarity": "common",
|
||||
"rarity_id": 1,
|
||||
"reflex_save": 0,
|
||||
"reflex_save_scale_number": 1,
|
||||
"release_date": "2024-03-27",
|
||||
"resistance": {},
|
||||
"search_markdown": "<traits>\r\n<trait label=\"Medium\" />\r\n<trait label=\"Mindless\" url=\"/Traits.aspx?ID=652\" />\r\n<trait label=\"Undead\" url=\"/Traits.aspx?ID=722\" />\r\n<trait label=\"Unholy\" url=\"/Traits.aspx?ID=521\" />\r\n<trait label=\"Zombie\" url=\"/Traits.aspx?ID=782\" />\r\n</traits>\r\n\r\n<additional-info>\r\n**Source** [Monster Core](/Sources.aspx?ID=221) pg. 356\r\n\r\n**Creature Family** [Zombie](/MonsterFamilies.aspx?ID=487)\r\n\r\n<row gap=\"medium\">\r\n**HP** 20 ( void healing )\r\n\r\n**AC** 12\r\n\r\n**Fort** +6\r\n\r\n**Ref** +0\r\n\r\n**Will** +2\r\n\r\n**Perception** +0\r\n</row>\r\n</additional-info>\r\n\r\n---\r\n\r\n<summary>\r\nA zombie shambler is a slow-moving horror dangerous in larger groups.\r\n</summary>",
|
||||
"sense_markdown": "[darkvision](/MonsterAbilities.aspx?ID=59)",
|
||||
"size": [
|
||||
"Medium"
|
||||
],
|
||||
"size_id": [
|
||||
3
|
||||
],
|
||||
"skill_markdown": "[Athletics](/Skills.aspx?ID=3) +7",
|
||||
"source": [
|
||||
"Monster Core"
|
||||
],
|
||||
"source_category": [
|
||||
"Rulebooks"
|
||||
],
|
||||
"source_markdown": "[Monster Core](/Sources.aspx?ID=221) pg. 356",
|
||||
"speed": {
|
||||
"land": 25,
|
||||
"max": 25
|
||||
},
|
||||
"speed_markdown": "25 feet",
|
||||
"spell_markdown": "",
|
||||
"strike_damage_average": [
|
||||
6
|
||||
],
|
||||
"strike_damage_scale_number": [
|
||||
5
|
||||
],
|
||||
"strength": 3,
|
||||
"strength_scale_number": 4,
|
||||
"strongest_save": [
|
||||
"fort",
|
||||
"fortitude"
|
||||
],
|
||||
"summary_markdown": "A zombie shambler is a slow-moving horror dangerous in larger groups.",
|
||||
"tradition_markdown": "",
|
||||
"trait": [
|
||||
"Mindless",
|
||||
"Undead",
|
||||
"Unholy",
|
||||
"Zombie",
|
||||
"Medium"
|
||||
],
|
||||
"trait_markdown": "[Mindless](/Traits.aspx?ID=652), [Undead](/Traits.aspx?ID=722), [Unholy](/Traits.aspx?ID=521), [Zombie](/Traits.aspx?ID=782)",
|
||||
"type": "Creature",
|
||||
"url": "/Monsters.aspx?ID=3249",
|
||||
"vision": "Darkvision",
|
||||
"weakest_save": [
|
||||
"ref",
|
||||
"reflex"
|
||||
],
|
||||
"weakness": {
|
||||
"slashing": 5,
|
||||
"vitality": 5
|
||||
},
|
||||
"weakness_markdown": "slashing 5, vitality 5",
|
||||
"will_save": 2,
|
||||
"will_save_scale_number": 2,
|
||||
"wisdom": 0,
|
||||
"wisdom_scale_number": 2
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
source: src/creature.rs
|
||||
expression: "&super::fix_links(s)"
|
||||
---
|
||||
blah blah [Foo Bar](https://2e.aonprd.com/Monsters.aspx?ID=2871) blah blah
|
||||
new line
|
||||
[More Links](https://2e.aonprd.com/Monsters.aspx?ID=2871)
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
source: src/creature.rs
|
||||
expression: "&obsidian"
|
||||
---
|
||||
```pf2e-stats
|
||||
# Tarrasque
|
||||
## Creature 25
|
||||
---
|
||||
|
||||
==Beast== ==Unique== ==CE== ==Gargantuan==
|
||||
|
||||
|
||||
**Perception** +48; darkvision, scent (imprecise) 120 feet
|
||||
|
||||
|
||||
**Skills**: [Acrobatics](https://2e.aonprd.com/Skills.aspx?ID=1) +47, [Athletics](https://2e.aonprd.com/Skills.aspx?ID=3) +50
|
||||
|
||||
|
||||
**Str** +12, **Dex** +9, **Con** +12, **Int** -3, **Wis** +9, **Cha** +7
|
||||
|
||||
|
||||
---
|
||||
|
||||
**AC** 54; **Fort** +47, **Ref** +37 **Will** +39
|
||||
**HP** 540; **Immunities** acid, clumsy, disease, drained, enfeebled, mental, paralyzed, persistent damage, petrified, poison, polymorph, stupefied; **Resistances** fire 25, physical 25
|
||||
**Carapace** Tarrasque is immune to cones, lines, rays, and [_magic missile_](https://2e.aonprd.com/Spells.aspx?ID=180) spells. These effects bounce harmlessly off its scales. **Frightful Presence** ([aura](https://2e.aonprd.com/Traits.aspx?ID=206), [emotion](https://2e.aonprd.com/Traits.aspx?ID=60), [fear](https://2e.aonprd.com/Traits.aspx?ID=68), [mental](https://2e.aonprd.com/Traits.aspx?ID=106)) 300 feet, DC 39 **Inexorable** Tarrasque recovers from the [slowed](https://2e.aonprd.com/Conditions.aspx?ID=35) and [stunned](https://2e.aonprd.com/Conditions.aspx?ID=36) conditions at the end of its turn. It’s also immune to penalties to its Speeds, and it ignores difficult terrain and greater difficult terrain. **Reactive** Tarrasque gains 3 reactions each round. It can still use only one reaction per trigger. **Attack of Opportunity** `[reaction]` **Reflect** `[reaction]` **Trigger** Tarrasque’s carapace deflects an effect. **Effect** The effect is redirected back at its source.
|
||||
|
||||
|
||||
---
|
||||
|
||||
**Speed**: 50 feet, swim 50 feet
|
||||
|
||||
**Melee** `[one-action]` jaws +45 ([Chaotic](https://2e.aonprd.com/Traits.aspx?ID=25), [Evil](https://2e.aonprd.com/Traits.aspx?ID=64), [Magical](https://2e.aonprd.com/Traits.aspx?ID=103), [reach 20 feet](https://2e.aonprd.com/Traits.aspx?ID=192)), **Damage** 5d12+20 piercing plus Improved Grab
|
||||
**Melee** `[one-action]` claw +45 ([Agile](https://2e.aonprd.com/Traits.aspx?ID=170), [Chaotic](https://2e.aonprd.com/Traits.aspx?ID=25), [Evil](https://2e.aonprd.com/Traits.aspx?ID=64), [Magical](https://2e.aonprd.com/Traits.aspx?ID=103), [reach 15 feet](https://2e.aonprd.com/Traits.aspx?ID=192)), **Damage** 5d10+20 slashing
|
||||
**Melee** `[one-action]` tail +45 ([Chaotic](https://2e.aonprd.com/Traits.aspx?ID=25), [Evil](https://2e.aonprd.com/Traits.aspx?ID=64), [Magical](https://2e.aonprd.com/Traits.aspx?ID=103), [reach 30 feet](https://2e.aonprd.com/Traits.aspx?ID=192)), **Damage** 4d12+20 bludgeoning
|
||||
**Melee** `[one-action]` horn +45 ([Chaotic](https://2e.aonprd.com/Traits.aspx?ID=25), [Evil](https://2e.aonprd.com/Traits.aspx?ID=64), [Magical](https://2e.aonprd.com/Traits.aspx?ID=103), [reach 15 feet](https://2e.aonprd.com/Traits.aspx?ID=192)), **Damage** 4d10+20 piercing
|
||||
**Ranged** `[one-action]` spine +45 ([Brutal](https://2e.aonprd.com/Traits.aspx?ID=249), [Chaotic](https://2e.aonprd.com/Traits.aspx?ID=25), [Evil](https://2e.aonprd.com/Traits.aspx?ID=64), [Magical](https://2e.aonprd.com/Traits.aspx?ID=103), [range increment 120 feet](https://2e.aonprd.com/Traits.aspx?ID=248)), **Damage** 3d10+20 piercing
|
||||
**Destructive Frenzy** `[three-actions]` Tarrasque makes a jaws Strike, two claw Strikes, two horn Strikes, and one tail Strike in any order.
|
||||
**Fast Swallow** `[reaction]` **Trigger** Tarrasque Grabs a creature with its jaws; **Effect** Tarrasque uses Swallow Whole.
|
||||
**Spine Volley** `[two-actions]` Tarrasque flings spines in a 120-foot cone, dealing 3d10+20 piercing damage to each creature in the area (DC 53 basic Reflex save). Tarrasque can’t use Spine Volley again for 1d4 rounds.
|
||||
**[Swallow Whole](https://2e.aonprd.com/MonsterAbilities.aspx?ID=35)** `[one-action]` ([Attack](https://2e.aonprd.com/Traits.aspx?ID=15)) Huge, 10d6+12 bludgeoning plus 10d6 acid, Rupture 50
|
||||
**[Trample](https://2e.aonprd.com/MonsterAbilities.aspx?ID=39)** `[three-actions]` Huge or smaller, claw, DC 49. When Tarrasque Tramples, it can Stride up to triple its Speed.
|
||||
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
source: src/creature.rs
|
||||
expression: "&obsidian"
|
||||
---
|
||||
```pf2e-stats
|
||||
# Zombie Shambler
|
||||
## Creature -1
|
||||
---
|
||||
|
||||
==Mindless== ==Undead== ==Unholy== ==Zombie== ==Medium==
|
||||
|
||||
|
||||
**Perception** +0; [darkvision](https://2e.aonprd.com/MonsterAbilities.aspx?ID=59)
|
||||
|
||||
|
||||
**Skills**: [Athletics](https://2e.aonprd.com/Skills.aspx?ID=3) +7
|
||||
|
||||
|
||||
**Str** +3, **Dex** -2, **Con** +2, **Int** -5, **Wis** +0, **Cha** -2
|
||||
**Slow** A zombie is permanently [slowed 1](https://2e.aonprd.com/Conditions.aspx?ID=92) and can't use reactions.
|
||||
|
||||
---
|
||||
|
||||
**AC** 12; **Fort** +6, **Ref** +0 **Will** +2
|
||||
**HP** 20 ( void healing ); **Immunities** bleed, [death](https://2e.aonprd.com/Traits.aspx?ID=571) effects, [disease](https://2e.aonprd.com/Traits.aspx?ID=578), [mental](https://2e.aonprd.com/Traits.aspx?ID=647), [paralyzed](https://2e.aonprd.com/Conditions.aspx?ID=85), [poison](https://2e.aonprd.com/Traits.aspx?ID=669), [unconscious](https://2e.aonprd.com/Conditions.aspx?ID=95); **Weaknesses** slashing 5, vitality 5
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
**Speed**: 25 feet
|
||||
|
||||
**Melee** `[one-action]` fist +7, **Damage** 1d6+3 bludgeoning plus [Grab](https://2e.aonprd.com/MonsterAbilities.aspx?ID=45)
|
||||
**Zombie Bite** `[one-action]` **Requirements** The zombie has a creature **grabbed** or [restrained](https://2e.aonprd.com/Conditions.aspx?ID=90); **Effect** The zombie makes a jaws [unarmed](https://2e.aonprd.com/Traits.aspx?ID=719) melee Strike against that creature with an attack modifier of +7 that deals 1d8+3 piercing damage.
|
||||
|
||||
```
|
||||
Reference in New Issue
Block a user