Fix links to AoN

This commit is contained in:
2026-05-17 22:42:37 +02:00
parent 5bd0efe95a
commit 2b095b0301
8 changed files with 747 additions and 10 deletions

352
Cargo.lock generated
View File

@@ -67,6 +67,18 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "bitflags"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clap"
version = "4.6.1"
@@ -113,12 +125,115 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "console"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
dependencies = [
"encode_unicode",
"libc",
"windows-sys",
]
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "getrandom"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasip2",
"wasip3",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "indexmap"
version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
"hashbrown 0.17.1",
"serde",
"serde_core",
]
[[package]]
name = "insta"
version = "1.47.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e"
dependencies = [
"console",
"once_cell",
"similar",
"tempfile",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -131,6 +246,30 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "memchr"
version = "2.8.0"
@@ -143,17 +282,34 @@ version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"insta",
"regex",
"serde",
"serde_json",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -172,6 +328,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "regex"
version = "1.12.3"
@@ -201,6 +363,25 @@ version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.228"
@@ -244,6 +425,12 @@ dependencies = [
"zmij",
]
[[package]]
name = "similar"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
[[package]]
name = "strsim"
version = "0.11.1"
@@ -261,18 +448,89 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "wasip2"
version = "1.0.3+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
dependencies = [
"wit-bindgen 0.57.1",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "windows-link"
version = "0.2.1"
@@ -288,6 +546,100 @@ dependencies = [
"windows-link",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "zmij"
version = "1.0.21"

View File

@@ -13,5 +13,8 @@ regex = "1.12.3"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
[dev-dependencies]
insta = "1.47.2"
# [target.'cfg(target_arch = "wasm32")'.dependencies]
# wasm-bindgen = "0.2.121"

View File

@@ -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

File diff suppressed because one or more lines are too long

111
src/creature2.example.json Normal file
View 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
}

View File

@@ -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)

View File

@@ -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. Its 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** Tarrasques 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 cant 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.
```

View File

@@ -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.
```