This commit is contained in:
TheGiddyLimit
2024-07-19 17:04:07 +01:00
parent 1e72254fcb
commit c330614db9
27 changed files with 1495 additions and 137 deletions

View File

@@ -1223,6 +1223,14 @@
"width": 3165, "width": 3165,
"height": 4096, "height": 4096,
"id": "4ab", "id": "4ab",
"grid": {
"type": "square",
"size": 165,
"offsetX": 25,
"offsetY": 48,
"scale": 2,
"distance": 10
},
"mapRegions": [ "mapRegions": [
{ {
"area": "03f", "area": "03f",
@@ -1873,6 +1881,14 @@
"credit": "Mike Schley", "credit": "Mike Schley",
"mapParent": { "mapParent": {
"id": "4ab" "id": "4ab"
},
"grid": {
"type": "square",
"size": 165,
"offsetX": 25,
"offsetY": 48,
"scale": 2,
"distance": 10
} }
} }
] ]
@@ -3262,6 +3278,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/018-map-2.02-buried-ziggurat-4.webp" "path": "adventure/QftIS/thumbnail/018-map-2.02-buried-ziggurat-4.webp"
},
"grid": {
"type": "square",
"size": 193,
"offsetX": 85,
"offsetY": -16,
"scale": 2,
"distance": 10
} }
}, },
{ {
@@ -3277,6 +3301,14 @@
"credit": "Mike Schley", "credit": "Mike Schley",
"mapParent": { "mapParent": {
"id": "59a" "id": "59a"
},
"grid": {
"type": "square",
"size": 193,
"offsetX": 85,
"offsetY": -16,
"scale": 2,
"distance": 10
} }
} }
] ]
@@ -4153,6 +4185,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/021-map-2.03-buried-ziggurat-5.webp" "path": "adventure/QftIS/thumbnail/021-map-2.03-buried-ziggurat-5.webp"
},
"grid": {
"type": "square",
"size": 147,
"offsetX": 34,
"offsetY": 6,
"scale": 2,
"distance": 10
} }
}, },
{ {
@@ -4168,6 +4208,14 @@
"credit": "Mike Schley", "credit": "Mike Schley",
"mapParent": { "mapParent": {
"id": "59b" "id": "59b"
},
"grid": {
"type": "square",
"size": 147,
"offsetX": 34,
"offsetY": 6,
"scale": 2,
"distance": 10
} }
} }
] ]
@@ -6033,6 +6081,13 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/025-map-2.04-expanded-ziggurat.webp" "path": "adventure/QftIS/thumbnail/025-map-2.04-expanded-ziggurat.webp"
},
"grid": {
"type": "square",
"size": 114,
"offsetX": -6,
"offsetY": 19,
"distance": 10
} }
}, },
{ {
@@ -6048,6 +6103,13 @@
"credit": "Mike Schley", "credit": "Mike Schley",
"mapParent": { "mapParent": {
"id": "59c" "id": "59c"
},
"grid": {
"type": "square",
"size": 114,
"offsetX": -6,
"offsetY": 19,
"distance": 10
} }
} }
] ]
@@ -6559,6 +6621,11 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/028-map-2.05-underground-city.webp" "path": "adventure/QftIS/thumbnail/028-map-2.05-underground-city.webp"
},
"grid": {
"type": "none",
"size": 148,
"distance": 200
} }
}, },
{ {
@@ -6574,6 +6641,11 @@
"credit": "Marco Bernardini", "credit": "Marco Bernardini",
"mapParent": { "mapParent": {
"id": "59d" "id": "59d"
},
"grid": {
"type": "none",
"size": 148,
"distance": 200
} }
} }
] ]
@@ -7228,6 +7300,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/033-map-3.01-tegefed-mountains.webp" "path": "adventure/QftIS/thumbnail/033-map-3.01-tegefed-mountains.webp"
},
"grid": {
"type": "hexColsOdd",
"size": 181,
"offsetX": -67,
"offsetY": 46,
"distance": 4,
"units": "miles"
} }
}, },
{ {
@@ -7243,6 +7323,14 @@
"credit": "Marco Bernardini", "credit": "Marco Bernardini",
"mapParent": { "mapParent": {
"id": "59e" "id": "59e"
},
"grid": {
"type": "hexColsOdd",
"size": 181,
"offsetX": -67,
"offsetY": 46,
"distance": 4,
"units": "miles"
} }
} }
] ]
@@ -7994,6 +8082,13 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/036-map-3.02-derwyths-homestead.webp" "path": "adventure/QftIS/thumbnail/036-map-3.02-derwyths-homestead.webp"
},
"grid": {
"type": "square",
"size": 167,
"offsetX": -42,
"offsetY": -47,
"distance": 10
} }
}, },
{ {
@@ -8009,6 +8104,13 @@
"credit": "Sean Macdonald", "credit": "Sean Macdonald",
"mapParent": { "mapParent": {
"id": "59f" "id": "59f"
},
"grid": {
"type": "square",
"size": 167,
"offsetX": -42,
"offsetY": -47,
"distance": 10
} }
} }
] ]
@@ -9463,6 +9565,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/039-map-3.03-derro-lair.webp" "path": "adventure/QftIS/thumbnail/039-map-3.03-derro-lair.webp"
},
"grid": {
"type": "square",
"size": 226,
"offsetX": 140,
"offsetY": 123,
"scale": 2,
"distance": 10
} }
}, },
{ {
@@ -9478,6 +9588,14 @@
"credit": "Jason A. Engle", "credit": "Jason A. Engle",
"mapParent": { "mapParent": {
"id": "5a0" "id": "5a0"
},
"grid": {
"type": "square",
"size": 226,
"offsetX": 140,
"offsetY": 123,
"scale": 2,
"distance": 10
} }
} }
] ]
@@ -9871,6 +9989,13 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/043-map-3.04-therno-lake.webp" "path": "adventure/QftIS/thumbnail/043-map-3.04-therno-lake.webp"
},
"grid": {
"type": "square",
"size": 306,
"offsetX": 111,
"offsetY": -89,
"distance": 50
} }
}, },
{ {
@@ -9886,6 +10011,13 @@
"credit": "Marco Bernardini", "credit": "Marco Bernardini",
"mapParent": { "mapParent": {
"id": "5a1" "id": "5a1"
},
"grid": {
"type": "square",
"size": 306,
"offsetX": 111,
"offsetY": -89,
"distance": 50
} }
} }
] ]
@@ -11298,6 +11430,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/046-map-3.05-tower-of-the-heavens.webp" "path": "adventure/QftIS/thumbnail/046-map-3.05-tower-of-the-heavens.webp"
},
"grid": {
"type": "square",
"size": 160,
"offsetX": -44,
"offsetY": 64,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -11313,6 +11453,14 @@
"credit": "Sean Macdonald", "credit": "Sean Macdonald",
"mapParent": { "mapParent": {
"id": "5a2" "id": "5a2"
},
"grid": {
"type": "square",
"size": 160,
"offsetX": -44,
"offsetY": 64,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -13562,6 +13710,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/050-map-3.06-forge-of-the-kagu-svirfneblin.webp" "path": "adventure/QftIS/thumbnail/050-map-3.06-forge-of-the-kagu-svirfneblin.webp"
},
"grid": {
"type": "square",
"size": 209,
"offsetX": 27,
"offsetY": 28,
"scale": 2,
"distance": 10
} }
}, },
{ {
@@ -13577,6 +13733,14 @@
"credit": "Damien Mammoliti", "credit": "Damien Mammoliti",
"mapParent": { "mapParent": {
"id": "5a3" "id": "5a3"
},
"grid": {
"type": "square",
"size": 209,
"offsetX": 27,
"offsetY": 28,
"scale": 2,
"distance": 10
} }
} }
] ]
@@ -15123,6 +15287,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/058-map-4.01-cave-of-echoes.webp" "path": "adventure/QftIS/thumbnail/058-map-4.01-cave-of-echoes.webp"
},
"grid": {
"type": "square",
"size": 201,
"offsetX": -21,
"offsetY": 21,
"scale": 2,
"distance": 10
} }
}, },
{ {
@@ -15138,6 +15310,14 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"mapParent": { "mapParent": {
"id": "5a4" "id": "5a4"
},
"grid": {
"type": "square",
"size": 201,
"offsetX": -21,
"offsetY": 21,
"scale": 2,
"distance": 10
} }
} }
] ]
@@ -16017,6 +16197,13 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/061-map-4.02-eternal-garden.webp" "path": "adventure/QftIS/thumbnail/061-map-4.02-eternal-garden.webp"
},
"grid": {
"type": "square",
"size": 81,
"offsetX": 24,
"offsetY": 29,
"distance": 50
} }
}, },
{ {
@@ -16032,6 +16219,13 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"mapParent": { "mapParent": {
"id": "5a5" "id": "5a5"
},
"grid": {
"type": "square",
"size": 81,
"offsetX": 24,
"offsetY": 29,
"distance": 50
} }
} }
] ]
@@ -17631,6 +17825,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/069-map-4.03-palace-of-spires.webp" "path": "adventure/QftIS/thumbnail/069-map-4.03-palace-of-spires.webp"
},
"grid": {
"type": "square",
"size": 209,
"offsetX": 95,
"offsetY": -18,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -17646,6 +17848,14 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"mapParent": { "mapParent": {
"id": "5a6" "id": "5a6"
},
"grid": {
"type": "square",
"size": 209,
"offsetX": 95,
"offsetY": -18,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -18914,6 +19124,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/076-map-5.01-false-tomb.webp" "path": "adventure/QftIS/thumbnail/076-map-5.01-false-tomb.webp"
},
"grid": {
"type": "square",
"size": 206,
"offsetX": 68,
"offsetY": -61,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -18929,6 +19147,14 @@
"credit": "Stacey Allan & William Doyle", "credit": "Stacey Allan & William Doyle",
"mapParent": { "mapParent": {
"id": "5a7" "id": "5a7"
},
"grid": {
"type": "square",
"size": 206,
"offsetX": 68,
"offsetY": -61,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -19953,6 +20179,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/079-map-5.02-maze-of-mists-map.webp" "path": "adventure/QftIS/thumbnail/079-map-5.02-maze-of-mists-map.webp"
},
"grid": {
"type": "square",
"size": 191,
"offsetX": -89,
"offsetY": -52,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -19968,6 +20202,14 @@
"credit": "Stacey Allan & William Doyle", "credit": "Stacey Allan & William Doyle",
"mapParent": { "mapParent": {
"id": "5a8" "id": "5a8"
},
"grid": {
"type": "square",
"size": 191,
"offsetX": -89,
"offsetY": -52,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -21724,6 +21966,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/082-map-5.04-halls-of-the-upper-priesthood-map.webp" "path": "adventure/QftIS/thumbnail/082-map-5.04-halls-of-the-upper-priesthood-map.webp"
},
"grid": {
"type": "square",
"size": 190,
"offsetX": -67,
"offsetY": -56,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -21739,6 +21989,14 @@
"credit": "Stacey Allan & William Doyle", "credit": "Stacey Allan & William Doyle",
"mapParent": { "mapParent": {
"id": "5a9" "id": "5a9"
},
"grid": {
"type": "square",
"size": 190,
"offsetX": -67,
"offsetY": -56,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -22207,6 +22465,9 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/084-map-5.05-dome-of-flight-map.webp" "path": "adventure/QftIS/thumbnail/084-map-5.05-dome-of-flight-map.webp"
},
"grid": {
"type": "none"
} }
}, },
{ {
@@ -22222,6 +22483,9 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"mapParent": { "mapParent": {
"id": "5ab" "id": "5ab"
},
"grid": {
"type": "none"
} }
} }
] ]
@@ -22470,7 +22734,10 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"width": 2400, "width": 2400,
"height": 3000, "height": 3000,
"id": "5ac" "id": "5ac",
"grid": {
"type": "none"
}
}, },
{ {
"type": "image", "type": "image",
@@ -22485,6 +22752,9 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"mapParent": { "mapParent": {
"id": "5ac" "id": "5ac"
},
"grid": {
"type": "none"
} }
} }
] ]
@@ -23518,6 +23788,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/089-map-5.07-gauntlet.webp" "path": "adventure/QftIS/thumbnail/089-map-5.07-gauntlet.webp"
},
"grid": {
"type": "square",
"size": 225,
"offsetX": 36,
"offsetY": -55,
"scale": 2,
"distance": 10
} }
}, },
{ {
@@ -23533,6 +23811,14 @@
"credit": "Stacey Allan & William Doyle", "credit": "Stacey Allan & William Doyle",
"mapParent": { "mapParent": {
"id": "5ad" "id": "5ad"
},
"grid": {
"type": "square",
"size": 225,
"offsetX": 36,
"offsetY": -55,
"scale": 2,
"distance": 10
} }
} }
] ]
@@ -23969,6 +24255,13 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/092-map-5.08-tomb-of-amun-sa-map.webp" "path": "adventure/QftIS/thumbnail/092-map-5.08-tomb-of-amun-sa-map.webp"
},
"grid": {
"type": "square",
"size": 87,
"offsetX": -10,
"offsetY": -36,
"distance": 10
} }
}, },
{ {
@@ -23984,6 +24277,13 @@
"credit": "Stacey Allan & William Doyle", "credit": "Stacey Allan & William Doyle",
"mapParent": { "mapParent": {
"id": "5ae" "id": "5ae"
},
"grid": {
"type": "square",
"size": 87,
"offsetX": -10,
"offsetY": -36,
"distance": 10
} }
} }
] ]
@@ -24426,7 +24726,16 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"width": 2550, "width": 2550,
"height": 3300, "height": 3300,
"id": "5af" "id": "5af",
"grid": {
"type": "hexRowsOdd",
"size": 240,
"offsetX": 142,
"offsetY": 98,
"scale": 3,
"distance": 4,
"units": "miles"
}
}, },
{ {
"type": "image", "type": "image",
@@ -24441,6 +24750,15 @@
"credit": "Marc Moureau", "credit": "Marc Moureau",
"mapParent": { "mapParent": {
"id": "5af" "id": "5af"
},
"grid": {
"type": "hexRowsOdd",
"size": 240,
"offsetX": 142,
"offsetY": 98,
"scale": 3,
"distance": 4,
"units": "miles"
} }
} }
] ]
@@ -29141,6 +29459,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/102-map-6.02-lesser-caverns-map.webp" "path": "adventure/QftIS/thumbnail/102-map-6.02-lesser-caverns-map.webp"
},
"grid": {
"type": "square",
"size": 211,
"offsetX": 34,
"offsetY": -42,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -29156,6 +29482,14 @@
"credit": "Mike Schley", "credit": "Mike Schley",
"mapParent": { "mapParent": {
"id": "5b0" "id": "5b0"
},
"grid": {
"type": "square",
"size": 211,
"offsetX": 34,
"offsetY": -42,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -35175,6 +35509,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/106-map-6.03-greater-caverns-map.webp" "path": "adventure/QftIS/thumbnail/106-map-6.03-greater-caverns-map.webp"
},
"grid": {
"type": "square",
"size": 193,
"offsetX": -40,
"offsetY": 95,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -35190,6 +35532,14 @@
"credit": "Mike Schley", "credit": "Mike Schley",
"mapParent": { "mapParent": {
"id": "5b1" "id": "5b1"
},
"grid": {
"type": "square",
"size": 193,
"offsetX": -40,
"offsetY": 95,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -38883,6 +39233,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/117-map-7.01-spaceship-level-1-map.webp" "path": "adventure/QftIS/thumbnail/117-map-7.01-spaceship-level-1-map.webp"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -38898,6 +39256,14 @@
"credit": "Damien Mammoliti", "credit": "Damien Mammoliti",
"mapParent": { "mapParent": {
"id": "5b2" "id": "5b2"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -41203,6 +41569,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/121-map-7.02-spaceship-level-2-map.webp" "path": "adventure/QftIS/thumbnail/121-map-7.02-spaceship-level-2-map.webp"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -41218,6 +41592,14 @@
"credit": "Damien Mammoliti", "credit": "Damien Mammoliti",
"mapParent": { "mapParent": {
"id": "5b3" "id": "5b3"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -43782,6 +44164,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/125-map-7.03-spaceship-level-3-map.webp" "path": "adventure/QftIS/thumbnail/125-map-7.03-spaceship-level-3-map.webp"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -43797,6 +44187,14 @@
"credit": "Damien Mammoliti", "credit": "Damien Mammoliti",
"mapParent": { "mapParent": {
"id": "5b4" "id": "5b4"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
} }
] ]
@@ -44507,6 +44905,14 @@
"hrefThumbnail": { "hrefThumbnail": {
"type": "internal", "type": "internal",
"path": "adventure/QftIS/thumbnail/129-map-7.04-spaceship-level-4-map.webp" "path": "adventure/QftIS/thumbnail/129-map-7.04-spaceship-level-4-map.webp"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
}, },
{ {
@@ -44522,6 +44928,14 @@
"credit": "Damien Mammoliti", "credit": "Damien Mammoliti",
"mapParent": { "mapParent": {
"id": "5b5" "id": "5b5"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
} }
} }
] ]

View File

@@ -3707,8 +3707,7 @@
"grid": { "grid": {
"type": "square", "type": "square",
"size": 66, "size": 66,
"offsetX": 17, "offsetX": -18,
"offsetY": 9,
"scale": 2 "scale": 2
}, },
"width": 1127, "width": 1127,

View File

@@ -7281,6 +7281,9 @@
}, },
{ {
"name": "Androsphinx", "name": "Androsphinx",
"group": [
"Sphinxes"
],
"source": "MM", "source": "MM",
"page": 281, "page": 281,
"srd": true, "srd": true,
@@ -19833,6 +19836,12 @@
"type": "internal", "type": "internal",
"path": "bestiary/svirfneblin.mp3" "path": "bestiary/svirfneblin.mp3"
}, },
"altArt": [
{
"name": "Svirfneblin",
"source": "QftIS"
}
],
"attachedItems": [ "attachedItems": [
"war pick|phb" "war pick|phb"
], ],
@@ -37055,6 +37064,9 @@
}, },
{ {
"name": "Gynosphinx", "name": "Gynosphinx",
"group": [
"Sphinxes"
],
"source": "MM", "source": "MM",
"page": 282, "page": 282,
"srd": true, "srd": true,

View File

@@ -1,5 +1,13 @@
{ {
"_meta": { "_meta": {
"dependencies": {
"monster": [
"MM"
]
},
"internalCopies": [
"monster"
],
"otherSources": { "otherSources": {
"monster": { "monster": {
"MM": "QftIS", "MM": "QftIS",
@@ -9,6 +17,25 @@
} }
}, },
"monster": [ "monster": [
{
"name": "Amun Sa",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Ghost",
"source": "MM",
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the ghost",
"with": "Amun",
"flags": "i"
}
}
},
"hasToken": true
},
{ {
"name": "Android", "name": "Android",
"source": "QftIS", "source": "QftIS",
@@ -160,8 +187,169 @@
"constitution", "constitution",
"strength" "strength"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true,
"_versions": [
{
"name": "Android Aerialist",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Design Specialization"
}
},
"speed": {
"walk": 30,
"fly": {
"number": 30,
"condition": "(hover)"
},
"canHover": true
},
"senses": [
"darkvision 60 ft."
],
"spellcasting": null,
"reaction": null,
"hasToken": true
},
{
"name": "Android Diplomat",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Design Specialization"
}
},
"speed": {
"walk": 30
},
"senses": [
"darkvision 60 ft."
],
"spellcasting": [
{
"name": "Spellcasting",
"type": "spellcasting",
"headerEntries": [
"The android casts one of the following spells, requiring no material components and using Intelligence as the spellcasting ability:"
],
"daily": {
"2e": [
"{@spell Identify}",
"{@spell Tongues}"
]
},
"ability": "int",
"displayAs": "action"
}
],
"reaction": null,
"hasToken": true
},
{
"name": "Android Diver",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Design Specialization"
}
},
"speed": {
"walk": 30,
"swim": 30
},
"senses": [
"darkvision 60 ft."
],
"spellcasting": null,
"reaction": null
},
{
"name": "Android Duelist",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Design Specialization"
},
"reaction": {
"mode": "renameArr",
"renames": {
"rename": "Parry (Duelist Only)",
"with": "Parry"
}
}
},
"speed": {
"walk": 30
},
"senses": [
"darkvision 60 ft."
],
"spellcasting": null,
"hasToken": true
},
{
"name": "Android Medic",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Design Specialization"
}
},
"speed": {
"walk": 30
},
"senses": [
"darkvision 60 ft."
],
"spellcasting": [
{
"name": "Spellcasting",
"type": "spellcasting",
"headerEntries": [
"The android casts one of the following spells, requiring no material components and using Intelligence as the spellcasting ability:"
],
"daily": {
"2e": [
"{@spell Cure Wounds} (as a 3rd-level spell)",
"{@spell Identify}"
]
},
"ability": "int",
"displayAs": "action"
}
],
"reaction": null,
"hasToken": true
},
{
"name": "Android Sentry",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Design Specialization"
}
},
"speed": {
"walk": 30
},
"senses": [
"blindsight 60 ft.",
"darkvision 60 ft."
],
"spellcasting": null,
"reaction": null,
"hasToken": true
}
]
}, },
{ {
"name": "Barkburr", "name": "Barkburr",
@@ -270,6 +458,7 @@
"savingThrowForced": [ "savingThrowForced": [
"constitution" "constitution"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -379,6 +568,7 @@
"RW", "RW",
"THW" "THW"
], ],
"hasToken": true,
"hasFluff": true "hasFluff": true
}, },
{ {
@@ -476,6 +666,7 @@
"MLW", "MLW",
"MW" "MW"
], ],
"hasToken": true,
"hasFluff": true "hasFluff": true
}, },
{ {
@@ -602,8 +793,38 @@
"dexterity", "dexterity",
"wisdom" "wisdom"
], ],
"hasToken": true,
"hasFluff": true "hasFluff": true
}, },
{
"name": "Cipolla",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Tower Sage",
"source": "QftIS",
"_templates": [
{
"name": "Dragonborn (Silver)",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the tower sage",
"with": "Cipolla",
"flags": "i"
}
}
},
"alignment": [
"L",
"E"
],
"hasToken": true
},
{ {
"name": "Combat Robot", "name": "Combat Robot",
"source": "QftIS", "source": "QftIS",
@@ -746,6 +967,7 @@
"constitution", "constitution",
"dexterity" "dexterity"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -875,6 +1097,7 @@
"savingThrowForcedSpell": [ "savingThrowForcedSpell": [
"wisdom" "wisdom"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -973,6 +1196,7 @@
"MW", "MW",
"RW" "RW"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -1112,6 +1336,7 @@
"constitution", "constitution",
"strength" "strength"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -1291,6 +1516,7 @@
"constitution", "constitution",
"wisdom" "wisdom"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -1441,6 +1667,7 @@
"intelligence", "intelligence",
"strength" "strength"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -1520,9 +1747,29 @@
"miscTags": [ "miscTags": [
"MW" "MW"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
{
"name": "Grisdelfawr",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Young Red Dragon",
"source": "MM",
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the dragon",
"with": "Grisdelfawr",
"flags": "i"
}
}
},
"hasToken": true
},
{ {
"name": "Guardian of Gorm", "name": "Guardian of Gorm",
"source": "QftIS", "source": "QftIS",
@@ -1606,6 +1853,7 @@
"RW", "RW",
"THW" "THW"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -1717,8 +1965,180 @@
"constitution", "constitution",
"dexterity" "dexterity"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true,
"_versions": [
{
"name": "Horrid Plant Dew Drinker",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Horrid Plant Varieties"
},
"bonus": [
{
"mode": "removeArr",
"names": [
"Sap Squirt (Purple Blossom Only)",
"Spiked Leaves (Snapper Saw Only)"
]
},
{
"mode": "renameArr",
"renames": {
"rename": "Vampiric Tendril (Dew Drinker Only)",
"with": "Vampiric Tendril"
}
}
]
}
},
{
"name": "Horrid Plant Purple Blossom",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Horrid Plant Varieties"
},
"bonus": [
{
"mode": "removeArr",
"names": [
"Spiked Leaves (Snapper Saw Only)",
"Vampiric Tendril (Dew Drinker Only)"
]
},
{
"mode": "renameArr",
"renames": {
"rename": "Sap Squirt (Purple Blossom Only)",
"with": "Sap Squirt"
}
}
]
}
},
{
"name": "Horrid Plant Snapper Saw",
"source": "QftIS",
"_mod": {
"trait": {
"mode": "removeArr",
"names": "Horrid Plant Varieties"
},
"bonus": [
{
"mode": "removeArr",
"names": [
"Sap Squirt (Purple Blossom Only)",
"Vampiric Tendril (Dew Drinker Only)"
]
},
{
"mode": "renameArr",
"renames": {
"rename": "Spiked Leaves (Snapper Saw Only)",
"with": "Spiked Leaves"
}
}
]
}
}
]
},
{
"name": "Iaseda",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Acolyte",
"source": "MM",
"_templates": [
{
"name": "Human",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the acolyte",
"with": "Iaseda",
"flags": "i"
}
}
},
"alignment": [
"L",
"G"
],
"hasToken": true
},
{
"name": "Isabela Folcarae",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Bandit Captain",
"source": "MM",
"_templates": [
{
"name": "Human",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the captain",
"with": "Isabela",
"flags": "i"
}
}
},
"alignment": [
"L",
"G"
],
"hasToken": true
},
{
"name": "Juliana",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Noble",
"source": "MM",
"_templates": [
{
"name": "Human",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the noble",
"with": "Juliana",
"flags": "i"
}
}
},
"alignment": [
"N",
"G"
],
"ac": [
11
],
"action": null,
"hasToken": true
}, },
{ {
"name": "Leprechaun", "name": "Leprechaun",
@@ -1875,6 +2295,7 @@
"savingThrowForcedSpell": [ "savingThrowForcedSpell": [
"intelligence" "intelligence"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -1964,6 +2385,7 @@
"dexterity", "dexterity",
"wisdom" "wisdom"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -2085,6 +2507,7 @@
"constitution", "constitution",
"dexterity" "dexterity"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -2187,6 +2610,7 @@
"savingThrowForced": [ "savingThrowForced": [
"intelligence" "intelligence"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -2418,6 +2842,7 @@
"savingThrowForced": [ "savingThrowForced": [
"strength" "strength"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -2609,9 +3034,43 @@
"constitution", "constitution",
"wisdom" "wisdom"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
{
"name": "Orlando",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Noble",
"source": "MM",
"_templates": [
{
"name": "Human",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the noble",
"with": "Orlando",
"flags": "i"
}
}
},
"alignment": [
"N",
"G"
],
"ac": [
11
],
"action": null,
"hasToken": true
},
{ {
"name": "Pech", "name": "Pech",
"source": "QftIS", "source": "QftIS",
@@ -2756,9 +3215,169 @@
"savingThrowForcedSpell": [ "savingThrowForcedSpell": [
"dexterity" "dexterity"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
{
"name": "Piyarz",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Tower Sage",
"source": "QftIS",
"_templates": [
{
"name": "Human",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the tower sage",
"with": "Piyarz",
"flags": "i"
},
"trait": {
"mode": "appendArr",
"items": {
"name": "Special Equipment",
"entries": [
"Piyarz wears a {@item Ring of Fire Resistance}, granting him resistance to fire damage."
]
}
}
}
},
"alignment": [
"L",
"E"
],
"resist": [
"fire"
],
"hasToken": true
},
{
"name": "Porro",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Tower Sage",
"source": "QftIS",
"_templates": [
{
"name": "Stout Halfling",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the tower sage",
"with": "Porro",
"flags": "i"
}
}
},
"alignment": [
"L",
"E"
],
"hasToken": true
},
{
"name": "Queen Zanobis",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Wight",
"source": "MM",
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the wight",
"with": "Queen Zanobis",
"flags": "i"
},
"action": [
{
"mode": "replaceArr",
"replace": "Multiattack",
"items": {
"name": "Multiattack",
"entries": [
"The wight makes two scepter attacks or two longbow attacks. It can use its Life Drain in place of one scepter attack."
]
}
},
{
"mode": "replaceArr",
"replace": "Longsword",
"items": {
"name": "Longsword",
"entries": [
"{@atk mw} {@hit 4} to hit, reach 5 ft., one target. {@h}6 ({@damage 1d8 + 2}) bludgeoning damage, or 7 ({@damage 1d10 + 2}) bludgeoning damage if used with two hands."
]
}
}
]
}
},
"hasToken": true
},
{
"name": "Shalfey",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Tower Sage",
"source": "QftIS",
"_templates": [
{
"name": "Human",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the tower sage",
"with": "Shalfey",
"flags": "i"
}
}
},
"alignment": [
"L",
"G"
],
"hasToken": true
},
{
"name": "Silverlily",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Unicorn",
"source": "MM",
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the unicorn",
"with": "Silverlily",
"flags": "i"
}
}
},
"hasToken": true
},
{ {
"name": "Sion", "name": "Sion",
"isNpc": true, "isNpc": true,
@@ -2918,9 +3537,48 @@
"savingThrowForced": [ "savingThrowForced": [
"charisma" "charisma"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
{
"name": "Spirit of Hunger",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Wraith",
"source": "MM",
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the wraith",
"with": "Spirit of Hunger",
"flags": "i"
}
}
},
"hasToken": true
},
{
"name": "Stargleam",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Unicorn",
"source": "MM",
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the unicorn",
"with": "Stargleam",
"flags": "i"
}
}
},
"hasToken": true
},
{ {
"name": "Swarm of Gibberlings", "name": "Swarm of Gibberlings",
"source": "QftIS", "source": "QftIS",
@@ -3022,6 +3680,7 @@
"miscTags": [ "miscTags": [
"MW" "MW"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -3226,6 +3885,7 @@
"savingThrowForced": [ "savingThrowForced": [
"constitution" "constitution"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -3322,6 +3982,7 @@
"MW", "MW",
"RW" "RW"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -3439,9 +4100,35 @@
"constitution", "constitution",
"dexterity" "dexterity"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
{
"name": "Uma",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Knight",
"source": "MM",
"_templates": [
{
"name": "Human",
"source": "PHB"
}
],
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the knight",
"with": "Uma",
"flags": "i"
}
}
},
"hasToken": true
},
{ {
"name": "Vegepygmy Moldmaker", "name": "Vegepygmy Moldmaker",
"source": "QftIS", "source": "QftIS",
@@ -3556,6 +4243,7 @@
"savingThrowForced": [ "savingThrowForced": [
"constitution" "constitution"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -3665,6 +4353,7 @@
"MW", "MW",
"RW" "RW"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -3764,9 +4453,29 @@
"savingThrowForced": [ "savingThrowForced": [
"strength" "strength"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
{
"name": "Vuuthramis",
"isNpc": true,
"isNamedCreature": true,
"source": "QftIS",
"_copy": {
"name": "Young Bronze Dragon",
"source": "MM",
"_mod": {
"*": {
"mode": "replaceTxt",
"replace": "the dragon",
"with": "Vuuthramis",
"flags": "i"
}
}
},
"hasToken": true
},
{ {
"name": "Warrior of Madarua", "name": "Warrior of Madarua",
"source": "QftIS", "source": "QftIS",
@@ -3844,6 +4553,7 @@
"RW", "RW",
"THW" "THW"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -3946,6 +4656,7 @@
"MW", "MW",
"RCH" "RCH"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -4053,6 +4764,7 @@
"savingThrowForced": [ "savingThrowForced": [
"constitution" "constitution"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
}, },
@@ -4243,6 +4955,7 @@
"constitution", "constitution",
"dexterity" "dexterity"
], ],
"hasToken": true,
"hasFluff": true, "hasFluff": true,
"hasFluffImages": true "hasFluffImages": true
} }

View File

@@ -275,6 +275,35 @@
} }
} }
}, },
{
"name": "Dragonborn (Silver)",
"source": "PHB",
"page": 32,
"ref": "{@race Dragonborn||Dragonborn (Silver)}",
"apply": {
"_root": {
"size": [
"M"
],
"type": {
"type": "humanoid",
"tags": [
"dragonborn"
]
}
},
"_mod": {
"resist": {
"mode": "appendIfNotExistsArr",
"items": "cold"
},
"languages": {
"mode": "appendIfNotExistsArr",
"items": "Draconic"
}
}
}
},
{ {
"name": "Drow", "name": "Drow",
"source": "PHB", "source": "PHB",
@@ -1677,6 +1706,25 @@
} }
} }
}, },
{
"name": "Human",
"source": "PHB",
"page": 29,
"ref": "{@race Human}",
"apply": {
"_root": {
"size": [
"M"
],
"type": {
"type": "humanoid",
"tags": [
"human"
]
}
}
}
},
{ {
"name": "Kenku", "name": "Kenku",
"source": "VGM", "source": "VGM",

View File

@@ -2730,11 +2730,16 @@
"ver": "1.209.0", "ver": "1.209.0",
"date": "2024-07-10", "date": "2024-07-10",
"title": "Mind the Step", "title": "Mind the Step",
"txt": "- Added Quests from the Infinite Staircase content\n- Overhauled partnered _[as in, \"partnered on D&D Beyond\"]_ content handling:\n - Added search indexing for partnered homebrew content. When the \"Include Homebrew\" option is enabled in Omnisearch, available partnered homebrew content will be included in the results.\n - Replaced \"Manage Homebrew\" with \"Manage Content\" button set, which includes options to \"Manage Prerelease Content\", \"Manage Homebrew\", and \"Load All Partnered Content\"\n - Added \"Load All Partnered Content\" button to navbar Utilities dropdown, along with an inline \"Export Prerelease Content/Homebrew List as URL\" button _[the latter was previously only accessible via the Prerelease Content Manager/Homebrew Manager UIs]_\n- Added \"Inflicts Curse\" and \"Inflicts Disease\" Bestiary \"Miscellaneous\" filters; added \"Mimicry\" to \"Traits\" filter\n- Added \"Consumable\" Items page \"Miscellaneous\" filter\n- Reworked Deities page `stats: ...` search functionality to search all available information\n- Added \"Download Markdown Data\" right-click option to pinned lists\n- Added middle-click to flip card in Decks page card viewer\n- Added page numbers to Tome of Beasts 1 (2023 Edition); The Book of Many Things\n- Fixed clicking a non-link navbar item resetting the URL hash\n- (Brew) When navigating to the list page link for a homebrew entity which is not yet loaded, that homebrew is _[mostly]_ transparently loaded and displayed\n- (Brew) Added warning when copying a Prerelease Content/Homebrew list URL if \"editable\" Prerelease Content/Homebrew is active\n- (Brew) Fixed Homebrew Builder failing to update/apply \"Source\" selectors\n- (Brew) Fixed crash on specific shared feat prerequisite sets\n- (Fixed typos/added tags)" "txt": "- Added Quests from the Infinite Staircase content\n- Overhauled partnered _[as in, \"partnered on D&D Beyond\"]_ content handling:\n - Added search indexing for partnered homebrew content. When the \"Include Homebrew\" option is enabled in Omnisearch, available partnered homebrew content will be included in the results.\n - Replaced \"Manage Homebrew\" with \"Manage Content\" button set, which includes options to \"Manage Prerelease Content\", \"Manage Homebrew\", and \"Load All Partnered Content\"\n - Added \"Load All Partnered Content\" button to navbar Utilities dropdown, along with an inline \"Export Prerelease Content/Homebrew List as URL\" button _[the latter was previously only accessible via the Prerelease Content Manager/Homebrew Manager UIs]_\n- Added \"Inflicts Curse\" and \"Inflicts Disease\" Bestiary \"Miscellaneous\" filters; added \"Mimicry\" to \"Traits\" filter\n- Added \"Consumable\" Items page \"Miscellaneous\" filter\n- Reworked Deities page `stats:...` search functionality to search all available information\n- Added \"Download Markdown Data\" right-click option to pinned lists\n- Added middle-click to flip card in Decks page card viewer\n- Added page numbers to Tome of Beasts 1 (2023 Edition); The Book of Many Things\n- Fixed clicking a non-link navbar item resetting the URL hash\n- (Brew) When navigating to the list page link for a homebrew entity which is not yet loaded, that homebrew is _[mostly]_ transparently loaded and displayed\n- (Brew) Added warning when copying a Prerelease Content/Homebrew list URL if \"editable\" Prerelease Content/Homebrew is active\n- (Brew) Fixed Homebrew Builder failing to update/apply \"Source\" selectors\n- (Brew) Fixed crash on specific shared feat prerequisite sets\n- (Fixed typos/added tags)"
}, },
{ {
"ver": "1.209.1", "ver": "1.209.1",
"date": "2024-07-10", "date": "2024-07-10",
"txt": "- Improved mobile footer layout on Bestiary, Items, Spells, and Psionics pages" "txt": "- Improved mobile footer layout on Bestiary, Items, Spells, and Psionics pages"
},
{
"ver": "1.209.2",
"date": "2024-07-19",
"txt": "- Added Quests from the Infinite Staircase tokens; NPCs/other creatures\n- Added `p`/`P` hotkeys to pin/unpin sublist items\n- Fixed Classes Page outline filling up with junk items after hovering other content\n- (Homebrew) Fixed Adventure/Book pages failing to load un-loaded but repo-available homebrew on navigation\n- (Fixed typos/added tags)"
} }
] ]

View File

@@ -214,7 +214,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Elven", "pantheon": "Elven",
@@ -239,7 +239,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Elven", "pantheon": "Elven",
@@ -1686,7 +1686,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Dwarven", "pantheon": "Dwarven",
@@ -1715,7 +1715,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Dwarven", "pantheon": "Dwarven",
@@ -1904,7 +1904,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
}, },
{ {
"source": "TCE", "source": "TCE",
@@ -1939,7 +1939,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
}, },
{ {
"source": "TCE", "source": "TCE",
@@ -2663,7 +2663,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Halfling", "pantheon": "Halfling",
@@ -2691,7 +2691,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Halfling", "pantheon": "Halfling",
@@ -3214,7 +3214,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Forgotten Realms", "pantheon": "Forgotten Realms",
@@ -3242,7 +3242,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Faerûnian", "pantheon": "Faerûnian",
@@ -4161,7 +4161,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Gnome", "pantheon": "Gnome",
@@ -4188,7 +4188,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Gnomish", "pantheon": "Gnomish",
@@ -10321,7 +10321,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Dragonlance", "pantheon": "Dragonlance",
@@ -11482,7 +11482,7 @@
"additionalSources": [ "additionalSources": [
{ {
"source": "TCE", "source": "TCE",
"page": 32 "page": 33
} }
], ],
"pantheon": "Greyhawk", "pantheon": "Greyhawk",
@@ -16677,6 +16677,10 @@
{ {
"source": "SCAG", "source": "SCAG",
"page": 125 "page": 125
},
{
"source": "TCE",
"page": 31
} }
], ],
"pantheon": "Greyhawk", "pantheon": "Greyhawk",

View File

@@ -45,7 +45,8 @@
"value": "5" "value": "5"
} }
], ],
"enchantmentRiderParent": "XY7pERdPxxb8bjlq" "enchantmentRiderParent": "XY7pERdPxxb8bjlq",
"transfer": true
}, },
{ {
"name": "Arcane Propulsion", "name": "Arcane Propulsion",
@@ -432,23 +433,14 @@
"mode": "ADD", "mode": "ADD",
"value": "1" "value": "1"
}, },
{
"key": "system.bonuses.msak.damage",
"mode": "ADD",
"value": "1"
},
{ {
"key": "system.bonuses.rsak.attack", "key": "system.bonuses.rsak.attack",
"mode": "ADD", "mode": "ADD",
"value": "1" "value": "1"
},
{
"key": "system.bonuses.rsak.damage",
"mode": "ADD",
"value": "1"
} }
], ],
"enchantmentRiderParent": "syx3ZXUoQwVJUZxP" "enchantmentRiderParent": "syx3ZXUoQwVJUZxP",
"transfer": true
}, },
{ {
"name": "Enhanced Arcane Focus +2", "name": "Enhanced Arcane Focus +2",
@@ -458,23 +450,14 @@
"mode": "ADD", "mode": "ADD",
"value": "2" "value": "2"
}, },
{
"key": "system.bonuses.msak.damage",
"mode": "ADD",
"value": "2"
},
{ {
"key": "system.bonuses.rsak.attack", "key": "system.bonuses.rsak.attack",
"mode": "ADD", "mode": "ADD",
"value": "2" "value": "2"
},
{
"key": "system.bonuses.rsak.damage",
"mode": "ADD",
"value": "2"
} }
], ],
"enchantmentRiderParent": "I4qB050rdgSsoIEd" "enchantmentRiderParent": "I4qB050rdgSsoIEd",
"transfer": true
} }
] ]
}, },
@@ -697,7 +680,8 @@
"mode": "ADD", "mode": "ADD",
"value": "+2" "value": "+2"
} }
] ],
"transfer": true
} }
] ]
}, },
@@ -799,7 +783,8 @@
"mode": "ADD", "mode": "ADD",
"value": "slashing" "value": "slashing"
} }
] ],
"transfer": true
} }
] ]
}, },

File diff suppressed because one or more lines are too long

View File

@@ -565,6 +565,9 @@ class BookUtil {
if (await this._booksHashChange_pDoLoadPrerelease({bookId, $contents, hashParts, isNewBook})) return; if (await this._booksHashChange_pDoLoadPrerelease({bookId, $contents, hashParts, isNewBook})) return;
if (await this._booksHashChange_pDoLoadBrew({bookId, $contents, hashParts, isNewBook})) return; if (await this._booksHashChange_pDoLoadBrew({bookId, $contents, hashParts, isNewBook})) return;
// if it's prerelease/homebrew but hasn't been loaded
if (await this._booksHashChange_pDoFetchPrereleaseBrew({bookId, $contents, hashParts, isNewBook})) return;
return this._booksHashChange_handleNotFound({$contents, bookId}); return this._booksHashChange_handleNotFound({$contents, bookId});
} }
@@ -595,6 +598,27 @@ class BookUtil {
return true; return true;
} }
static async _booksHashChange_pDoFetchPrereleaseBrew ({bookId, $contents, hashParts, isNewBook}) {
const {source} = await UrlUtil.pAutoDecodeHash(bookId);
const loaded = await DataLoader.pCacheAndGetHash(UrlUtil.getCurrentPage(), bookId, {isSilent: true});
if (!loaded) return false;
return [
PrereleaseUtil,
BrewUtil2,
]
.some(brewUtil => {
if (
brewUtil.hasSourceJson(source)
&& brewUtil.isReloadRequired()
) {
brewUtil.doLocationReload({isRetainHash: true});
return true;
}
});
}
static _booksHashChange_getCleanName (fromIndex) { static _booksHashChange_getCleanName (fromIndex) {
if (fromIndex.parentSource) { if (fromIndex.parentSource) {
const fullParentSource = Parser.sourceJsonToFull(fromIndex.parentSource); const fullParentSource = Parser.sourceJsonToFull(fromIndex.parentSource);

View File

@@ -88,41 +88,42 @@ class UtilClassesPage {
Renderer.get().setFirstSection(true); Renderer.get().setFirstSection(true);
if (hasEntries) { if (hasEntries) {
const renderer = Renderer.get(); Renderer.get().withDepthTracker(
depthArr || [],
({renderer}) => {
entFluff.entries.filter(f => f.source === ent.source).forEach(f => f._isStandardSource = true);
if (depthArr) renderer.setDepthTracker(depthArr, {additionalPropsInherited: ["_isStandardSource"]}); entFluff.entries.forEach((f, i) => {
else renderer.setDepthTracker([]); const cpy = MiscUtil.copyFast(f);
entFluff.entries.filter(f => f.source === ent.source).forEach(f => f._isStandardSource = true); // Remove the name from the first section if it is a copy of the class/subclass name
if (
isRemoveRootName
&& i === 0
&& cpy.name
&& (
cpy.name.toLowerCase() === ent.name.toLowerCase()
|| cpy.name.toLowerCase() === `the ${ent.name.toLowerCase()}`
)
) {
delete cpy.name;
}
entFluff.entries.forEach((f, i) => { if (
const cpy = MiscUtil.copyFast(f); isAddSourceNote
&& typeof cpy !== "string"
&& cpy.source
&& cpy.source !== ent.source
&& cpy.entries
) {
cpy.entries.unshift(`{@note The following information is from ${Parser.sourceJsonToFull(cpy.source)}${Renderer.utils.isDisplayPage(cpy.page) ? `, page ${cpy.page}` : ""}.}`);
}
// Remove the name from the first section if it is a copy of the class/subclass name stack += renderer.render(cpy);
if ( });
isRemoveRootName },
&& i === 0 {additionalPropsInherited: ["_isStandardSource"]},
&& cpy.name );
&& (
cpy.name.toLowerCase() === ent.name.toLowerCase()
|| cpy.name.toLowerCase() === `the ${ent.name.toLowerCase()}`
)
) {
delete cpy.name;
}
if (
isAddSourceNote
&& typeof cpy !== "string"
&& cpy.source
&& cpy.source !== ent.source
&& cpy.entries
) {
cpy.entries.unshift(`{@note The following information is from ${Parser.sourceJsonToFull(cpy.source)}${Renderer.utils.isDisplayPage(cpy.page) ? `, page ${cpy.page}` : ""}.}`);
}
stack += renderer.render(cpy);
});
} }
if (hasImages) { if (hasImages) {
@@ -2158,9 +2159,15 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
if (source === cls.source) return {isSkip: true}; if (source === cls.source) return {isSkip: true};
}, },
fn: () => { fn: () => {
return $(`<tr data-scroll-id="${ixLvl}-${ixFeature}" data-feature-type="class" class="cls-main__linked-titles"><td colspan="6"></td></tr>`) return Renderer.get().withDepthTracker(
.fastSetHtml(Renderer.get().setDepthTracker(depthArr, {additionalProps: ["isReprinted"], additionalPropsInherited: ["_isStandardSource", "isClassFeatureVariant"]}).render(feature)) depthArr,
.appendTo($content); ({renderer}) => {
return $(`<tr data-scroll-id="${ixLvl}-${ixFeature}" data-feature-type="class" class="cls-main__linked-titles"><td colspan="6"></td></tr>`)
.fastSetHtml(renderer.render(feature))
.appendTo($content);
},
{additionalProps: ["isReprinted"], additionalPropsInherited: ["_isStandardSource", "isClassFeatureVariant"]},
);
}, },
}); });
this._trackOutlineCfData(ixLvl, ixFeature, depthArr); this._trackOutlineCfData(ixLvl, ixFeature, depthArr);
@@ -2173,7 +2180,7 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
// Add a placeholder feature to display when no subclasses are active // Add a placeholder feature to display when no subclasses are active
const $trSubclassFeature = $(`<tr class="cls-main__sc-feature" data-subclass-none-message="true"><td colspan="6"></td></tr>`) const $trSubclassFeature = $(`<tr class="cls-main__sc-feature" data-subclass-none-message="true"><td colspan="6"></td></tr>`)
.fastSetHtml(Renderer.get().setDepthTracker([]).render({type: "entries", entries: [{name: `{@note No Subclass Selected}`, type: "entries", entries: [`{@note <span class="clickable roller" data-jump-select-a-subclass="true">Select a subclass</span> to view its feature(s) here.}`]}]})) .fastSetHtml(Renderer.get().withDepthTracker([], ({renderer}) => renderer.render({type: "entries", entries: [{name: `{@note No Subclass Selected}`, type: "entries", entries: [`{@note <span class="clickable roller" data-jump-select-a-subclass="true">Select a subclass</span> to view its feature(s) here.}`]}]})))
.appendTo($content); .appendTo($content);
await cls.subclasses.pSerialAwaitMap(async sc => { await cls.subclasses.pSerialAwaitMap(async sc => {
@@ -2224,7 +2231,9 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
}, },
fn: () => { fn: () => {
const $trSubclassFeature = $(`<tr class="cls-main__sc-feature" data-subclass-id="${UrlUtil.getStateKeySubclass(sc)}"><td colspan="6"></td></tr>`) const $trSubclassFeature = $(`<tr class="cls-main__sc-feature" data-subclass-id="${UrlUtil.getStateKeySubclass(sc)}"><td colspan="6"></td></tr>`)
.fastSetHtml(Renderer.get().setDepthTracker(depthArr, {additionalProps: ["isReprinted"], additionalPropsInherited: ["_isStandardSource", "isClassFeatureVariant"]}).render(toRender)) .fastSetHtml(
Renderer.get().withDepthTracker(depthArr, ({renderer}) => renderer.render(toRender), {additionalProps: ["isReprinted"], additionalPropsInherited: ["_isStandardSource", "isClassFeatureVariant"]}),
)
.appendTo($content); .appendTo($content);
}, },
}); });

View File

@@ -422,11 +422,11 @@ class SublistManager {
await this.pDoSublistRemove({entity, doFinalize: true}); await this.pDoSublistRemove({entity, doFinalize: true});
} }
getTitleBtnAdd () { return `Add (SHIFT for ${this._shiftCountAddSubtract})`; } getTitleBtnAdd () { return `Add (SHIFT for ${this._shiftCountAddSubtract}) (Hotkey: p)`; }
getTitleBtnSubtract () { return `Subtract (SHIFT for ${this._shiftCountAddSubtract})`; } getTitleBtnSubtract () { return `Subtract (SHIFT for ${this._shiftCountAddSubtract}) (Hotkey: P)`; }
async pHandleClick_btnAdd ({evt, entity}) { async pHandleClick_btnAdd ({entity, isMultiple = false}) {
const addCount = evt.shiftKey ? this._shiftCountAddSubtract : 1; const addCount = isMultiple ? this._shiftCountAddSubtract : 1;
return this.pDoSublistAdd({ return this.pDoSublistAdd({
index: Hist.lastLoadedId, index: Hist.lastLoadedId,
entity, entity,
@@ -435,8 +435,8 @@ class SublistManager {
}); });
} }
async pHandleClick_btnSubtract ({evt, entity}) { async pHandleClick_btnSubtract ({entity, isMultiple = false}) {
const subtractCount = evt.shiftKey ? this._shiftCountAddSubtract : 1; const subtractCount = isMultiple ? this._shiftCountAddSubtract : 1;
return this.pDoSublistSubtract({ return this.pDoSublistSubtract({
index: Hist.lastLoadedId, index: Hist.lastLoadedId,
entity, entity,
@@ -1567,7 +1567,7 @@ class ListPage {
const key = EventUtil.getKeyIgnoreCapsLock(evt); const key = EventUtil.getKeyIgnoreCapsLock(evt);
switch (key) { switch (key) {
// K up; J down // k up; j down
case "k": case "k":
case "j": { case "j": {
// don't switch if the user is typing somewhere else // don't switch if the user is typing somewhere else
@@ -1576,6 +1576,24 @@ class ListPage {
return; return;
} }
// p: toggle pinned/add 1 to sublist
case "p": {
if (EventUtil.isInInput(evt)) return;
if (!this._sublistManager) return;
if (this._sublistManager.isSublistItemsCountable) this._sublistManager.pHandleClick_btnAdd({entity: this._lastRender.entity}).then(null);
else this._sublistManager.pHandleClick_btnPin({entity: this._lastRender.entity}).then(null);
return;
}
// P: toggle pinned/remove 1 from sublist
case "P": {
if (EventUtil.isInInput(evt)) return;
if (!this._sublistManager) return;
if (this._sublistManager.isSublistItemsCountable) this._sublistManager.pHandleClick_btnSubtract({entity: this._lastRender.entity}).then(null);
else this._sublistManager.pHandleClick_btnPin({entity: this._lastRender.entity}).then(null);
return;
}
// m: expand/collapse current selection
case "m": { case "m": {
if (EventUtil.isInInput(evt)) return; if (EventUtil.isInInput(evt)) return;
const it = Hist.getSelectedListElementWithLocation(); const it = Hist.getSelectedListElementWithLocation();
@@ -1735,21 +1753,21 @@ class ListPage {
this._getOrTabRightButton(`pin`, `pushpin`) this._getOrTabRightButton(`pin`, `pushpin`)
.off("click") .off("click")
.on("click", () => this._sublistManager.pHandleClick_btnPin({entity: this._lastRender.entity})) .on("click", () => this._sublistManager.pHandleClick_btnPin({entity: this._lastRender.entity}))
.title("Pin (Toggle)"); .title("Pin (Toggle) (Hotkey: p/P)");
} }
_bindAddButton () { _bindAddButton () {
this._getOrTabRightButton(`sublist-add`, `plus`) this._getOrTabRightButton(`sublist-add`, `plus`)
.off("click") .off("click")
.title(this._sublistManager.getTitleBtnAdd()) .title(this._sublistManager.getTitleBtnAdd())
.on("click", evt => this._sublistManager.pHandleClick_btnAdd({evt, entity: this._lastRender.entity})); .on("click", evt => this._sublistManager.pHandleClick_btnAdd({entity: this._lastRender.entity, isMultiple: !!evt.shiftKey}));
} }
_bindSubtractButton () { _bindSubtractButton () {
this._getOrTabRightButton(`sublist-subtract`, `minus`) this._getOrTabRightButton(`sublist-subtract`, `minus`)
.off("click") .off("click")
.title(this._sublistManager.getTitleBtnSubtract()) .title(this._sublistManager.getTitleBtnSubtract())
.on("click", evt => this._sublistManager.pHandleClick_btnSubtract({evt, entity: this._lastRender.entity})); .on("click", evt => this._sublistManager.pHandleClick_btnSubtract({entity: this._lastRender.entity, isMultiple: !!evt.shiftKey}));
} }
/** /**

View File

@@ -915,8 +915,7 @@ class IndexableFileQuickReference extends IndexableFile {
static getChapterNameMetas (it, {isRequireQuickrefFlag = true} = {}) { static getChapterNameMetas (it, {isRequireQuickrefFlag = true} = {}) {
const trackedNames = []; const trackedNames = [];
const renderer = Renderer.get().setDepthTracker(trackedNames); Renderer.get().withDepthTracker(trackedNames, ({renderer}) => renderer.render(it));
renderer.render(it);
const nameCounts = {}; const nameCounts = {};
trackedNames.forEach(meta => { trackedNames.forEach(meta => {

View File

@@ -243,6 +243,50 @@ globalThis.Renderer = function () {
}); });
}; };
/**
* Specify an array where the renderer will record rendered header depths.
* Items added to the array are of the form: `{name: "Header Name", depth: 1, type: "entries", source: "PHB"}`
* @param arr
* @param additionalProps Additional data props which should be tracked per-entry.
* @param additionalPropsInherited As per additionalProps, but if a parent entry has the prop, it should be passed
* to its children.
*/
this.setDepthTracker = function (arr, {additionalProps, additionalPropsInherited} = {}) {
this._depthTracker = arr;
this._depthTrackerAdditionalProps = additionalProps || [];
this._depthTrackerAdditionalPropsInherited = additionalPropsInherited || [];
return this;
};
this.withDepthTracker = function (arr, fn, {additionalProps, additionalPropsInherited} = {}) {
const depthTrackerPrev = this._depthTracker;
const depthTrackerAdditionalPropsPrev = this._depthTrackerAdditionalProps;
const depthTrackerAdditionalPropsInheritedPrev = this._depthTrackerAdditionalPropsInherited;
let out;
try {
this.setDepthTracker(
arr,
{
additionalProps,
additionalPropsInherited,
},
);
out = fn({renderer: this});
} finally {
this.setDepthTracker(
depthTrackerPrev,
{
additionalProps: depthTrackerAdditionalPropsPrev,
additionalPropsInherited: depthTrackerAdditionalPropsInheritedPrev,
},
);
}
return out;
};
/* -------------------------------------------- */
// region Plugins // region Plugins
this.addPlugin = function (pluginType, fnPlugin) { this.addPlugin = function (pluginType, fnPlugin) {
MiscUtil.getOrSet(this._plugins, pluginType, []).push(fnPlugin); MiscUtil.getOrSet(this._plugins, pluginType, []).push(fnPlugin);
@@ -309,21 +353,6 @@ globalThis.Renderer = function () {
}; };
// endregion // endregion
/**
* Specify an array where the renderer will record rendered header depths.
* Items added to the array are of the form: `{name: "Header Name", depth: 1, type: "entries", source: "PHB"}`
* @param arr
* @param additionalProps Additional data props which should be tracked per-entry.
* @param additionalPropsInherited As per additionalProps, but if a parent entry has the prop, it should be passed
* to its children.
*/
this.setDepthTracker = function (arr, {additionalProps, additionalPropsInherited} = {}) {
this._depthTracker = arr;
this._depthTrackerAdditionalProps = additionalProps || [];
this._depthTrackerAdditionalPropsInherited = additionalPropsInherited || [];
return this;
};
this.getLineBreak = function () { return "<br>"; }; this.getLineBreak = function () { return "<br>"; };
/** /**
@@ -1431,19 +1460,26 @@ globalThis.Renderer = function () {
}; };
this._renderStatblock = function (entry, textStack, meta, options) { this._renderStatblock = function (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
const page = entry.prop || Renderer.tag.getPage(entry.tag); const page = entry.prop || Renderer.tag.getPage(entry.tag);
const source = Parser.getTagSource(entry.tag, entry.source); const source = Parser.getTagSource(entry.tag, entry.source);
const hash = entry.hash || (UrlUtil.URL_TO_HASH_BUILDER[page] ? UrlUtil.URL_TO_HASH_BUILDER[page]({...entry, name: entry.name, source}) : null); const hash = entry.hash || (UrlUtil.URL_TO_HASH_BUILDER[page] ? UrlUtil.URL_TO_HASH_BUILDER[page]({...entry, name: entry.name, source}) : null);
const tag = entry.tag || Parser.getPropTag(entry.prop);
const asTag = `{@${entry.tag} ${entry.name}|${source}${entry.displayName ? `|${entry.displayName}` : ""}}`; const asTag = `{@${tag} ${entry.name}|${source}${entry.displayName ? `|${entry.displayName}` : ""}}`;
const fromPlugins = this._applyPlugins_useFirst(
"statblock_render",
{textStack, meta, options},
{input: {entry, page, source, hash, tag, asTag}},
);
if (fromPlugins) return void (textStack[0] += fromPlugins);
if (!page || !source || !hash) { if (!page || !source || !hash) {
this._renderPrefix(entry, textStack, meta, options);
this._renderDataHeader(textStack, entry.name, entry.style); this._renderDataHeader(textStack, entry.name, entry.style);
textStack[0] += `<tr> textStack[0] += `<tr>
<td colspan="6"> <td colspan="6">
<i class="text-danger">Cannot load ${entry.tag ? `&quot;${asTag}&quot;` : entry.displayName || entry.name}! An unknown tag/prop, source, or hash was provided.</i> <i class="text-danger">Cannot load ${tag ? `&quot;${asTag}&quot;` : entry.displayName || entry.name}! An unknown tag/prop, source, or hash was provided.</i>
</td> </td>
</tr>`; </tr>`;
this._renderDataFooter(textStack); this._renderDataFooter(textStack);
@@ -1452,10 +1488,11 @@ globalThis.Renderer = function () {
return; return;
} }
this._renderPrefix(entry, textStack, meta, options);
this._renderDataHeader(textStack, entry.displayName || entry.name, entry.style, {isCollapsed: entry.collapsed}); this._renderDataHeader(textStack, entry.displayName || entry.name, entry.style, {isCollapsed: entry.collapsed});
textStack[0] += `<tr> textStack[0] += `<tr>
<td colspan="6" data-rd-tag="${(entry.tag || "").qq()}" data-rd-page="${(page || "").qq()}" data-rd-source="${(source || "").qq()}" data-rd-hash="${(hash || "").qq()}" data-rd-name="${(entry.name || "").qq()}" data-rd-display-name="${(entry.displayName || "").qq()}" data-rd-style="${(entry.style || "").qq()}"> <td colspan="6" data-rd-tag="${(tag || "").qq()}" data-rd-page="${(page || "").qq()}" data-rd-source="${(source || "").qq()}" data-rd-hash="${(hash || "").qq()}" data-rd-name="${(entry.name || "").qq()}" data-rd-display-name="${(entry.displayName || "").qq()}" data-rd-style="${(entry.style || "").qq()}">
<i>Loading ${entry.tag ? `${Renderer.get().render(asTag)}` : entry.displayName || entry.name}...</i> <i>Loading ${tag ? `${Renderer.get().render(asTag)}` : entry.displayName || entry.name}...</i>
<style onload="Renderer.events.handleLoad_inlineStatblock(this)"></style> <style onload="Renderer.events.handleLoad_inlineStatblock(this)"></style>
</td> </td>
</tr>`; </tr>`;
@@ -10494,7 +10531,7 @@ Renderer.recipe = class {
? `{@b {@style Makes|small-caps}} ${ent._scaleFactor ? `${ent._scaleFactor}× ` : ""}${ent.makes}` ? `{@b {@style Makes|small-caps}} ${ent._scaleFactor ? `${ent._scaleFactor}× ` : ""}${ent.makes}`
: null, : null,
entryServes: ent.serves entryServes: ent.serves
? `{@b {@style Serves|small-caps}} ${ent.serves.min ?? ent.serves.exact}${ent.serves.min != null ? " to " : ""}${ent.serves.max ?? ""}` ? `{@b {@style Serves|small-caps}} ${ent.serves.min ?? ent.serves.exact}${ent.serves.min != null ? " to " : ""}${ent.serves.max ?? ""}${ent.serves.note ? ` ${ent.serves.note}` : ""}`
: null, : null,
entryMetasTime: Renderer.recipe._getEntryMetasTime(ent), entryMetasTime: Renderer.recipe._getEntryMetasTime(ent),
entryIngredients: {entries: ent._fullIngredients}, entryIngredients: {entries: ent._fullIngredients},
@@ -10680,6 +10717,7 @@ Renderer.recipe = class {
"tablespoon", "tablespoon",
"teaspoon", "teaspoon",
"wedge", "wedge",
"fist",
]; ];
static _UNITS_SINGLE_TO_PLURAL_ES = [ static _UNITS_SINGLE_TO_PLURAL_ES = [
"dash", "dash",

View File

@@ -113,9 +113,11 @@ export class BrewUtil2Base {
isReloadRequired () { return this._isDirty; } isReloadRequired () { return this._isDirty; }
doLocationReload () { doLocationReload ({isRetainHash = false} = {}) {
if (typeof Hist !== "undefined") Hist.doPreLocationReload(); if (!isRetainHash) {
else window.location.hash = ""; if (typeof Hist !== "undefined") Hist.doPreLocationReload();
else window.location.hash = "";
}
location.reload(); location.reload();
} }
@@ -513,6 +515,8 @@ export class BrewUtil2Base {
pLoadPropIndex (urlRoot) { throw new Error("Unimplemented!"); } pLoadPropIndex (urlRoot) { throw new Error("Unimplemented!"); }
/** @abstract */ /** @abstract */
pLoadMetaIndex (urlRoot) { throw new Error("Unimplemented!"); } pLoadMetaIndex (urlRoot) { throw new Error("Unimplemented!"); }
/** @abstract */
pLoadAdventureBookIdsIndex (urlRoot) { throw new Error("Unimplemented!"); }
async pGetCombinedIndexes () { async pGetCombinedIndexes () {
const urlRoot = await this.pGetCustomUrl(); const urlRoot = await this.pGetCustomUrl();

View File

@@ -89,6 +89,8 @@ export class BrewUtil2_ extends BrewUtil2Base {
pLoadMetaIndex (urlRoot) { return DataUtil.brew.pLoadMetaIndex(urlRoot); } pLoadMetaIndex (urlRoot) { return DataUtil.brew.pLoadMetaIndex(urlRoot); }
pLoadAdventureBookIdsIndex (urlRoot) { return DataUtil.brew.pLoadAdventureBookIdsIndex(urlRoot); }
/* -------------------------------------------- */ /* -------------------------------------------- */
// region Editable // region Editable

View File

@@ -41,6 +41,8 @@ export class PrereleaseUtil_ extends BrewUtil2Base {
pLoadMetaIndex (urlRoot) { return DataUtil.prerelease.pLoadMetaIndex(urlRoot); } pLoadMetaIndex (urlRoot) { return DataUtil.prerelease.pLoadMetaIndex(urlRoot); }
pLoadAdventureBookIdsIndex (urlRoot) { return DataUtil.prerelease.pLoadAdventureBookIdsIndex(urlRoot); }
/* -------------------------------------------- */ /* -------------------------------------------- */
// region Editable // region Editable

View File

@@ -1987,7 +1987,7 @@ class DataLoader {
} }
static async pCacheAndGetHash (page, hash, opts) { static async pCacheAndGetHash (page, hash, opts) {
const {source} = UrlUtil.autoDecodeHash(hash, {page}); const {source} = await UrlUtil.pAutoDecodeHash(hash, {page});
if (!source) { if (!source) {
if (opts.isRequired) throw new Error(`Could not find entity for page "${page}" with hash "${hash}"`); if (opts.isRequired) throw new Error(`Could not find entity for page "${page}" with hash "${hash}"`);
return null; return null;

View File

@@ -1907,6 +1907,8 @@ PropOrder._VARIANTRULE = [
"type", "type",
"entries", "entries",
"foundryImg",
]; ];
PropOrder._RACE_SUBRACE = [ PropOrder._RACE_SUBRACE = [
"page", "page",

View File

@@ -965,7 +965,7 @@ class ListUiUtil {
let elePreviewWrp; let elePreviewWrp;
if (item.ele.children.length === 1) { if (item.ele.children.length === 1) {
elePreviewWrp = e_({ elePreviewWrp = e_({
ag: "div", tag: "div",
clazz: "ve-hidden ve-flex", clazz: "ve-hidden ve-flex",
children: [ children: [
e_({tag: "div", clazz: "ve-col-0-5"}), e_({tag: "div", clazz: "ve-col-0-5"}),

View File

@@ -2,7 +2,7 @@
// in deployment, `IS_DEPLOYED = "<version number>";` should be set below. // in deployment, `IS_DEPLOYED = "<version number>";` should be set below.
globalThis.IS_DEPLOYED = undefined; globalThis.IS_DEPLOYED = undefined;
globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.209.1"/* 5ETOOLS_VERSION__CLOSE */; globalThis.VERSION_NUMBER = /* 5ETOOLS_VERSION__OPEN */"1.209.2"/* 5ETOOLS_VERSION__CLOSE */;
globalThis.DEPLOYED_IMG_ROOT = undefined; globalThis.DEPLOYED_IMG_ROOT = undefined;
// for the roll20 script to set // for the roll20 script to set
globalThis.IS_VTT = false; globalThis.IS_VTT = false;
@@ -2812,7 +2812,24 @@ globalThis.UrlUtil = {
return hash.split(HASH_LIST_SEP).map(it => decodeURIComponent(it)); return hash.split(HASH_LIST_SEP).map(it => decodeURIComponent(it));
}, },
/* -------------------------------------------- */
/**
* @param hash
* @param {?string} page
*/
async pAutoDecodeHash (hash, {page = null} = {}) {
page ||= UrlUtil.getCurrentPage();
if ([UrlUtil.PG_ADVENTURE, UrlUtil.PG_BOOK].includes(page)) return UrlUtil._pAutoDecodeHashAdventureBookHash(hash, {page});
return UrlUtil.autoDecodeHash(hash, {page});
},
// TODO(Future) expand // TODO(Future) expand
/**
* @param hash
* @param {?string} page
*/
autoDecodeHash (hash, {page = null} = {}) { autoDecodeHash (hash, {page = null} = {}) {
page ||= UrlUtil.getCurrentPage(); page ||= UrlUtil.getCurrentPage();
const parts = UrlUtil.decodeHash(hash.toLowerCase().trim()); const parts = UrlUtil.decodeHash(hash.toLowerCase().trim());
@@ -2822,10 +2839,57 @@ globalThis.UrlUtil = {
return {name, pantheon, source}; return {name, pantheon, source};
} }
// TODO(Future) this is broken for docs where the id != the source
// consider indexing
// + homebrew
if (page === UrlUtil.PG_ADVENTURE || page === UrlUtil.PG_BOOK) {
const [source] = parts;
return {source};
}
const [name, source] = parts; const [name, source] = parts;
return {name, source}; return {name, source};
}, },
/**
* @param hash
* @param {?string} page
*/
async _pAutoDecodeHashAdventureBookHash (hash, {page = null} = {}) {
page ||= UrlUtil.getCurrentPage();
const parts = UrlUtil.decodeHash(hash.toLowerCase().trim());
if (![UrlUtil.PG_ADVENTURE, UrlUtil.PG_BOOK].includes(page)) throw new Error(`Unhandled page "${page}"!`);
const [id] = parts;
for (const {prop, contentsUrl} of [
{
prop: "adventure",
contentsUrl: `${Renderer.get().baseUrl}data/adventures.json`,
},
{
prop: "book",
contentsUrl: `${Renderer.get().baseUrl}data/books.json`,
},
]) {
const contents = await DataUtil.loadJSON(contentsUrl);
const ent = contents[prop].find(it => it.id.toLowerCase() === id);
if (ent) return {name: ent.name, source: ent.source, id: ent.id};
}
for (const brewUtil of [PrereleaseUtil, BrewUtil2]) {
const urlRoot = await brewUtil.pGetCustomUrl();
const idsIndex = await brewUtil.pLoadAdventureBookIdsIndex(urlRoot);
if (idsIndex[id]) return idsIndex[id];
}
return {};
},
/* -------------------------------------------- */
getSluggedHash (hash) { getSluggedHash (hash) {
return Parser.stringToSlug(decodeURIComponent(hash)).replace(/_/g, "-"); return Parser.stringToSlug(decodeURIComponent(hash)).replace(/_/g, "-");
}, },
@@ -3758,6 +3822,11 @@ class _DataUtilBrewHelper {
return DataUtil.loadJSON(`${urlRoot}_generated/index-sources.json`); return DataUtil.loadJSON(`${urlRoot}_generated/index-sources.json`);
} }
async pLoadAdventureBookIdsIndex (urlRoot) {
urlRoot = this._getCleanUrlRoot(urlRoot);
return DataUtil.loadJSON(`${urlRoot}_generated/index-adventure-book-ids.json`);
}
getFileUrl (path, urlRoot) { getFileUrl (path, urlRoot) {
urlRoot = this._getCleanUrlRoot(urlRoot); urlRoot = this._getCleanUrlRoot(urlRoot);
return `${urlRoot}${path}`; return `${urlRoot}${path}`;

18
package-lock.json generated
View File

@@ -1,15 +1,15 @@
{ {
"name": "5etools", "name": "5etools",
"version": "1.209.1", "version": "1.209.2",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "5etools", "name": "5etools",
"version": "1.209.1", "version": "1.209.2",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"5etools-utils": "^0.12.19", "5etools-utils": "^0.12.20",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"ajv-formats": "^2.1.1", "ajv-formats": "^2.1.1",
"commander": "^12.0.0", "commander": "^12.0.0",
@@ -3926,9 +3926,9 @@
"dev": true "dev": true
}, },
"node_modules/5etools-utils": { "node_modules/5etools-utils": {
"version": "0.12.19", "version": "0.12.20",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.19.tgz", "resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.20.tgz",
"integrity": "sha512-0VyiX585H93CxKshYX7sUq0mqTjmBUQSe0AgeCpD9MqCq3wTtQCs1nIFBxjb8zQR1DqNDUc+JiMLfmEe/XHGug==", "integrity": "sha512-PA5ZMMId+jvoUJvr19eNOq3skfTw2h57iHR4hO3YU2YDKOojtLB35vbasayZ7zJmoelDIqyAk/LF4j/lDpTZrg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ajv": "^8.12.0", "ajv": "^8.12.0",
@@ -14589,9 +14589,9 @@
"dev": true "dev": true
}, },
"5etools-utils": { "5etools-utils": {
"version": "0.12.19", "version": "0.12.20",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.19.tgz", "resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.20.tgz",
"integrity": "sha512-0VyiX585H93CxKshYX7sUq0mqTjmBUQSe0AgeCpD9MqCq3wTtQCs1nIFBxjb8zQR1DqNDUc+JiMLfmEe/XHGug==", "integrity": "sha512-PA5ZMMId+jvoUJvr19eNOq3skfTw2h57iHR4hO3YU2YDKOojtLB35vbasayZ7zJmoelDIqyAk/LF4j/lDpTZrg==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^8.12.0", "ajv": "^8.12.0",

View File

@@ -1,7 +1,7 @@
{ {
"name": "5etools", "name": "5etools",
"author": "TheGiddyLimit", "author": "TheGiddyLimit",
"version": "1.209.1", "version": "1.209.2",
"license": "MIT", "license": "MIT",
"description": "A site dedicated to making playing games with your friends as easy as possible.", "description": "A site dedicated to making playing games with your friends as easy as possible.",
"type": "module", "type": "module",
@@ -46,7 +46,7 @@
"url": "git+https://github.com/5etools-mirror-2/5etools-mirror-2.github.io.git" "url": "git+https://github.com/5etools-mirror-2/5etools-mirror-2.github.io.git"
}, },
"devDependencies": { "devDependencies": {
"5etools-utils": "^0.12.19", "5etools-utils": "^0.12.20",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"ajv-formats": "^2.1.1", "ajv-formats": "^2.1.1",
"commander": "^12.0.0", "commander": "^12.0.0",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -2,6 +2,7 @@ import fs from "fs";
import path from "path"; import path from "path";
import "../js/parser.js"; import "../js/parser.js";
import "../js/utils.js"; import "../js/utils.js";
import "../js/render.js";
import * as ut from "../node/util.js"; import * as ut from "../node/util.js";
import {listFiles} from "../node/util.js"; import {listFiles} from "../node/util.js";
@@ -41,6 +42,8 @@ class _TestTokenImages {
.forEach(file => { .forEach(file => {
ut.readJson(`./data/bestiary/${file}`).monster ut.readJson(`./data/bestiary/${file}`).monster
.forEach(m => { .forEach(m => {
m.__prop = "monster";
const implicitTokenPath = `${this._PATH_BASE}/${m.source}/${Parser.nameToTokenName(m.name)}.${this._EXT}`; const implicitTokenPath = `${this._PATH_BASE}/${m.source}/${Parser.nameToTokenName(m.name)}.${this._EXT}`;
if (m.hasToken) this._expectedFromHashToken[implicitTokenPath] = true; if (m.hasToken) this._expectedFromHashToken[implicitTokenPath] = true;
@@ -59,6 +62,14 @@ class _TestTokenImages {
.forEach(entry => this._expected.add(`${this._PATH_BASE}/${entry.token.source}/${Parser.nameToTokenName(entry.token.name)}.${this._EXT}`)); .forEach(entry => this._expected.add(`${this._PATH_BASE}/${entry.token.source}/${Parser.nameToTokenName(entry.token.name)}.${this._EXT}`));
} }
// add tokens specified as part of versions
const versions = DataUtil.proxy.getVersions(m.__prop, m, {isExternalApplicationIdentityOnly: true});
versions
.forEach(mVer => {
if (!Renderer.monster.hasToken(mVer)) return;
this._expected.add(`${this._PATH_BASE}/${mVer.source}/${Parser.nameToTokenName(mVer.name)}.${this._EXT}`);
});
// add tokens specified as alt art // add tokens specified as alt art
if (m.altArt) { if (m.altArt) {
m.altArt m.altArt