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
+416 -2
View File
@@ -1223,6 +1223,14 @@
"width": 3165,
"height": 4096,
"id": "4ab",
"grid": {
"type": "square",
"size": 165,
"offsetX": 25,
"offsetY": 48,
"scale": 2,
"distance": 10
},
"mapRegions": [
{
"area": "03f",
@@ -1873,6 +1881,14 @@
"credit": "Mike Schley",
"mapParent": {
"id": "4ab"
},
"grid": {
"type": "square",
"size": 165,
"offsetX": 25,
"offsetY": 48,
"scale": 2,
"distance": 10
}
}
]
@@ -3262,6 +3278,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "59a"
},
"grid": {
"type": "square",
"size": 193,
"offsetX": 85,
"offsetY": -16,
"scale": 2,
"distance": 10
}
}
]
@@ -4153,6 +4185,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "59b"
},
"grid": {
"type": "square",
"size": 147,
"offsetX": 34,
"offsetY": 6,
"scale": 2,
"distance": 10
}
}
]
@@ -6033,6 +6081,13 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "59c"
},
"grid": {
"type": "square",
"size": 114,
"offsetX": -6,
"offsetY": 19,
"distance": 10
}
}
]
@@ -6559,6 +6621,11 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "59d"
},
"grid": {
"type": "none",
"size": 148,
"distance": 200
}
}
]
@@ -7228,6 +7300,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "59e"
},
"grid": {
"type": "hexColsOdd",
"size": 181,
"offsetX": -67,
"offsetY": 46,
"distance": 4,
"units": "miles"
}
}
]
@@ -7994,6 +8082,13 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "59f"
},
"grid": {
"type": "square",
"size": 167,
"offsetX": -42,
"offsetY": -47,
"distance": 10
}
}
]
@@ -9463,6 +9565,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a0"
},
"grid": {
"type": "square",
"size": 226,
"offsetX": 140,
"offsetY": 123,
"scale": 2,
"distance": 10
}
}
]
@@ -9871,6 +9989,13 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a1"
},
"grid": {
"type": "square",
"size": 306,
"offsetX": 111,
"offsetY": -89,
"distance": 50
}
}
]
@@ -11298,6 +11430,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a2"
},
"grid": {
"type": "square",
"size": 160,
"offsetX": -44,
"offsetY": 64,
"scale": 3,
"distance": 10
}
}
]
@@ -13562,6 +13710,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a3"
},
"grid": {
"type": "square",
"size": 209,
"offsetX": 27,
"offsetY": 28,
"scale": 2,
"distance": 10
}
}
]
@@ -15123,6 +15287,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a4"
},
"grid": {
"type": "square",
"size": 201,
"offsetX": -21,
"offsetY": 21,
"scale": 2,
"distance": 10
}
}
]
@@ -16017,6 +16197,13 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a5"
},
"grid": {
"type": "square",
"size": 81,
"offsetX": 24,
"offsetY": 29,
"distance": 50
}
}
]
@@ -17631,6 +17825,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a6"
},
"grid": {
"type": "square",
"size": 209,
"offsetX": 95,
"offsetY": -18,
"scale": 3,
"distance": 10
}
}
]
@@ -18914,6 +19124,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a7"
},
"grid": {
"type": "square",
"size": 206,
"offsetX": 68,
"offsetY": -61,
"scale": 3,
"distance": 10
}
}
]
@@ -19953,6 +20179,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a8"
},
"grid": {
"type": "square",
"size": 191,
"offsetX": -89,
"offsetY": -52,
"scale": 3,
"distance": 10
}
}
]
@@ -21724,6 +21966,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5a9"
},
"grid": {
"type": "square",
"size": 190,
"offsetX": -67,
"offsetY": -56,
"scale": 3,
"distance": 10
}
}
]
@@ -22207,6 +22465,9 @@
"hrefThumbnail": {
"type": "internal",
"path": "adventure/QftIS/thumbnail/084-map-5.05-dome-of-flight-map.webp"
},
"grid": {
"type": "none"
}
},
{
@@ -22222,6 +22483,9 @@
"credit": "Marc Moureau",
"mapParent": {
"id": "5ab"
},
"grid": {
"type": "none"
}
}
]
@@ -22470,7 +22734,10 @@
"credit": "Marc Moureau",
"width": 2400,
"height": 3000,
"id": "5ac"
"id": "5ac",
"grid": {
"type": "none"
}
},
{
"type": "image",
@@ -22485,6 +22752,9 @@
"credit": "Marc Moureau",
"mapParent": {
"id": "5ac"
},
"grid": {
"type": "none"
}
}
]
@@ -23518,6 +23788,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5ad"
},
"grid": {
"type": "square",
"size": 225,
"offsetX": 36,
"offsetY": -55,
"scale": 2,
"distance": 10
}
}
]
@@ -23969,6 +24255,13 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5ae"
},
"grid": {
"type": "square",
"size": 87,
"offsetX": -10,
"offsetY": -36,
"distance": 10
}
}
]
@@ -24426,7 +24726,16 @@
"credit": "Marc Moureau",
"width": 2550,
"height": 3300,
"id": "5af"
"id": "5af",
"grid": {
"type": "hexRowsOdd",
"size": 240,
"offsetX": 142,
"offsetY": 98,
"scale": 3,
"distance": 4,
"units": "miles"
}
},
{
"type": "image",
@@ -24441,6 +24750,15 @@
"credit": "Marc Moureau",
"mapParent": {
"id": "5af"
},
"grid": {
"type": "hexRowsOdd",
"size": 240,
"offsetX": 142,
"offsetY": 98,
"scale": 3,
"distance": 4,
"units": "miles"
}
}
]
@@ -29141,6 +29459,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5b0"
},
"grid": {
"type": "square",
"size": 211,
"offsetX": 34,
"offsetY": -42,
"scale": 3,
"distance": 10
}
}
]
@@ -35175,6 +35509,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5b1"
},
"grid": {
"type": "square",
"size": 193,
"offsetX": -40,
"offsetY": 95,
"scale": 3,
"distance": 10
}
}
]
@@ -38883,6 +39233,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5b2"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
}
}
]
@@ -41203,6 +41569,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5b3"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
}
}
]
@@ -43782,6 +44164,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5b4"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
}
}
]
@@ -44507,6 +44905,14 @@
"hrefThumbnail": {
"type": "internal",
"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",
"mapParent": {
"id": "5b5"
},
"grid": {
"type": "square",
"size": 218,
"offsetX": -76,
"offsetY": 26,
"scale": 3,
"distance": 10
}
}
]
+1 -2
View File
@@ -3707,8 +3707,7 @@
"grid": {
"type": "square",
"size": 66,
"offsetX": 17,
"offsetY": 9,
"offsetX": -18,
"scale": 2
},
"width": 1127,
+12
View File
@@ -7281,6 +7281,9 @@
},
{
"name": "Androsphinx",
"group": [
"Sphinxes"
],
"source": "MM",
"page": 281,
"srd": true,
@@ -19833,6 +19836,12 @@
"type": "internal",
"path": "bestiary/svirfneblin.mp3"
},
"altArt": [
{
"name": "Svirfneblin",
"source": "QftIS"
}
],
"attachedItems": [
"war pick|phb"
],
@@ -37055,6 +37064,9 @@
},
{
"name": "Gynosphinx",
"group": [
"Sphinxes"
],
"source": "MM",
"page": 282,
"srd": true,
+715 -2
View File
@@ -1,5 +1,13 @@
{
"_meta": {
"dependencies": {
"monster": [
"MM"
]
},
"internalCopies": [
"monster"
],
"otherSources": {
"monster": {
"MM": "QftIS",
@@ -9,6 +17,25 @@
}
},
"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",
"source": "QftIS",
@@ -160,8 +187,169 @@
"constitution",
"strength"
],
"hasToken": 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",
@@ -270,6 +458,7 @@
"savingThrowForced": [
"constitution"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -379,6 +568,7 @@
"RW",
"THW"
],
"hasToken": true,
"hasFluff": true
},
{
@@ -476,6 +666,7 @@
"MLW",
"MW"
],
"hasToken": true,
"hasFluff": true
},
{
@@ -602,8 +793,38 @@
"dexterity",
"wisdom"
],
"hasToken": 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",
"source": "QftIS",
@@ -746,6 +967,7 @@
"constitution",
"dexterity"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -875,6 +1097,7 @@
"savingThrowForcedSpell": [
"wisdom"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -973,6 +1196,7 @@
"MW",
"RW"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -1112,6 +1336,7 @@
"constitution",
"strength"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -1291,6 +1516,7 @@
"constitution",
"wisdom"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -1441,6 +1667,7 @@
"intelligence",
"strength"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -1520,9 +1747,29 @@
"miscTags": [
"MW"
],
"hasToken": true,
"hasFluff": 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",
"source": "QftIS",
@@ -1606,6 +1853,7 @@
"RW",
"THW"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -1717,8 +1965,180 @@
"constitution",
"dexterity"
],
"hasToken": 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",
@@ -1875,6 +2295,7 @@
"savingThrowForcedSpell": [
"intelligence"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -1964,6 +2385,7 @@
"dexterity",
"wisdom"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -2085,6 +2507,7 @@
"constitution",
"dexterity"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -2187,6 +2610,7 @@
"savingThrowForced": [
"intelligence"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -2418,6 +2842,7 @@
"savingThrowForced": [
"strength"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -2609,9 +3034,43 @@
"constitution",
"wisdom"
],
"hasToken": true,
"hasFluff": 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",
"source": "QftIS",
@@ -2756,9 +3215,169 @@
"savingThrowForcedSpell": [
"dexterity"
],
"hasToken": true,
"hasFluff": 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",
"isNpc": true,
@@ -2918,9 +3537,48 @@
"savingThrowForced": [
"charisma"
],
"hasToken": true,
"hasFluff": 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",
"source": "QftIS",
@@ -3022,6 +3680,7 @@
"miscTags": [
"MW"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -3226,6 +3885,7 @@
"savingThrowForced": [
"constitution"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -3322,6 +3982,7 @@
"MW",
"RW"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -3439,9 +4100,35 @@
"constitution",
"dexterity"
],
"hasToken": true,
"hasFluff": 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",
"source": "QftIS",
@@ -3556,6 +4243,7 @@
"savingThrowForced": [
"constitution"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -3665,6 +4353,7 @@
"MW",
"RW"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -3764,9 +4453,29 @@
"savingThrowForced": [
"strength"
],
"hasToken": true,
"hasFluff": 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",
"source": "QftIS",
@@ -3844,6 +4553,7 @@
"RW",
"THW"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -3946,6 +4656,7 @@
"MW",
"RCH"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -4053,6 +4764,7 @@
"savingThrowForced": [
"constitution"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
},
@@ -4243,6 +4955,7 @@
"constitution",
"dexterity"
],
"hasToken": true,
"hasFluff": true,
"hasFluffImages": true
}
+48
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",
"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",
"source": "VGM",
+6 -1
View File
@@ -2730,11 +2730,16 @@
"ver": "1.209.0",
"date": "2024-07-10",
"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",
"date": "2024-07-10",
"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)"
}
]
+18 -14
View File
@@ -214,7 +214,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Elven",
@@ -239,7 +239,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Elven",
@@ -1686,7 +1686,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Dwarven",
@@ -1715,7 +1715,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Dwarven",
@@ -1904,7 +1904,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
},
{
"source": "TCE",
@@ -1939,7 +1939,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
},
{
"source": "TCE",
@@ -2663,7 +2663,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Halfling",
@@ -2691,7 +2691,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Halfling",
@@ -3214,7 +3214,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Forgotten Realms",
@@ -3242,7 +3242,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Faerûnian",
@@ -4161,7 +4161,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Gnome",
@@ -4188,7 +4188,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Gnomish",
@@ -10321,7 +10321,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Dragonlance",
@@ -11482,7 +11482,7 @@
"additionalSources": [
{
"source": "TCE",
"page": 32
"page": 33
}
],
"pantheon": "Greyhawk",
@@ -16677,6 +16677,10 @@
{
"source": "SCAG",
"page": 125
},
{
"source": "TCE",
"page": 31
}
],
"pantheon": "Greyhawk",
+10 -25
View File
@@ -45,7 +45,8 @@
"value": "5"
}
],
"enchantmentRiderParent": "XY7pERdPxxb8bjlq"
"enchantmentRiderParent": "XY7pERdPxxb8bjlq",
"transfer": true
},
{
"name": "Arcane Propulsion",
@@ -432,23 +433,14 @@
"mode": "ADD",
"value": "1"
},
{
"key": "system.bonuses.msak.damage",
"mode": "ADD",
"value": "1"
},
{
"key": "system.bonuses.rsak.attack",
"mode": "ADD",
"value": "1"
},
{
"key": "system.bonuses.rsak.damage",
"mode": "ADD",
"value": "1"
}
],
"enchantmentRiderParent": "syx3ZXUoQwVJUZxP"
"enchantmentRiderParent": "syx3ZXUoQwVJUZxP",
"transfer": true
},
{
"name": "Enhanced Arcane Focus +2",
@@ -458,23 +450,14 @@
"mode": "ADD",
"value": "2"
},
{
"key": "system.bonuses.msak.damage",
"mode": "ADD",
"value": "2"
},
{
"key": "system.bonuses.rsak.attack",
"mode": "ADD",
"value": "2"
},
{
"key": "system.bonuses.rsak.damage",
"mode": "ADD",
"value": "2"
}
],
"enchantmentRiderParent": "I4qB050rdgSsoIEd"
"enchantmentRiderParent": "I4qB050rdgSsoIEd",
"transfer": true
}
]
},
@@ -697,7 +680,8 @@
"mode": "ADD",
"value": "+2"
}
]
],
"transfer": true
}
]
},
@@ -799,7 +783,8 @@
"mode": "ADD",
"value": "slashing"
}
]
],
"transfer": true
}
]
},
File diff suppressed because one or more lines are too long
+24
View File
@@ -565,6 +565,9 @@ class BookUtil {
if (await this._booksHashChange_pDoLoadPrerelease({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});
}
@@ -595,6 +598,27 @@ class BookUtil {
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) {
if (fromIndex.parentSource) {
const fullParentSource = Parser.sourceJsonToFull(fromIndex.parentSource);
+45 -36
View File
@@ -88,41 +88,42 @@ class UtilClassesPage {
Renderer.get().setFirstSection(true);
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"]});
else renderer.setDepthTracker([]);
entFluff.entries.forEach((f, i) => {
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) => {
const cpy = MiscUtil.copyFast(f);
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}` : ""}.}`);
}
// 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;
}
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);
});
stack += renderer.render(cpy);
});
},
{additionalPropsInherited: ["_isStandardSource"]},
);
}
if (hasImages) {
@@ -2158,9 +2159,15 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
if (source === cls.source) return {isSkip: true};
},
fn: () => {
return $(`<tr data-scroll-id="${ixLvl}-${ixFeature}" data-feature-type="class" class="cls-main__linked-titles"><td colspan="6"></td></tr>`)
.fastSetHtml(Renderer.get().setDepthTracker(depthArr, {additionalProps: ["isReprinted"], additionalPropsInherited: ["_isStandardSource", "isClassFeatureVariant"]}).render(feature))
.appendTo($content);
return Renderer.get().withDepthTracker(
depthArr,
({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);
@@ -2173,7 +2180,7 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
// 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>`)
.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);
await cls.subclasses.pSerialAwaitMap(async sc => {
@@ -2224,7 +2231,9 @@ class ClassesPage extends MixinComponentGlobalState(MixinBaseComponent(MixinProx
},
fn: () => {
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);
},
});
+28 -10
View File
@@ -422,11 +422,11 @@ class SublistManager {
await this.pDoSublistRemove({entity, doFinalize: true});
}
getTitleBtnAdd () { return `Add (SHIFT for ${this._shiftCountAddSubtract})`; }
getTitleBtnSubtract () { return `Subtract (SHIFT for ${this._shiftCountAddSubtract})`; }
getTitleBtnAdd () { return `Add (SHIFT for ${this._shiftCountAddSubtract}) (Hotkey: p)`; }
getTitleBtnSubtract () { return `Subtract (SHIFT for ${this._shiftCountAddSubtract}) (Hotkey: P)`; }
async pHandleClick_btnAdd ({evt, entity}) {
const addCount = evt.shiftKey ? this._shiftCountAddSubtract : 1;
async pHandleClick_btnAdd ({entity, isMultiple = false}) {
const addCount = isMultiple ? this._shiftCountAddSubtract : 1;
return this.pDoSublistAdd({
index: Hist.lastLoadedId,
entity,
@@ -435,8 +435,8 @@ class SublistManager {
});
}
async pHandleClick_btnSubtract ({evt, entity}) {
const subtractCount = evt.shiftKey ? this._shiftCountAddSubtract : 1;
async pHandleClick_btnSubtract ({entity, isMultiple = false}) {
const subtractCount = isMultiple ? this._shiftCountAddSubtract : 1;
return this.pDoSublistSubtract({
index: Hist.lastLoadedId,
entity,
@@ -1567,7 +1567,7 @@ class ListPage {
const key = EventUtil.getKeyIgnoreCapsLock(evt);
switch (key) {
// K up; J down
// k up; j down
case "k":
case "j": {
// don't switch if the user is typing somewhere else
@@ -1576,6 +1576,24 @@ class ListPage {
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": {
if (EventUtil.isInInput(evt)) return;
const it = Hist.getSelectedListElementWithLocation();
@@ -1735,21 +1753,21 @@ class ListPage {
this._getOrTabRightButton(`pin`, `pushpin`)
.off("click")
.on("click", () => this._sublistManager.pHandleClick_btnPin({entity: this._lastRender.entity}))
.title("Pin (Toggle)");
.title("Pin (Toggle) (Hotkey: p/P)");
}
_bindAddButton () {
this._getOrTabRightButton(`sublist-add`, `plus`)
.off("click")
.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 () {
this._getOrTabRightButton(`sublist-subtract`, `minus`)
.off("click")
.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}));
}
/**
+1 -2
View File
@@ -915,8 +915,7 @@ class IndexableFileQuickReference extends IndexableFile {
static getChapterNameMetas (it, {isRequireQuickrefFlag = true} = {}) {
const trackedNames = [];
const renderer = Renderer.get().setDepthTracker(trackedNames);
renderer.render(it);
Renderer.get().withDepthTracker(trackedNames, ({renderer}) => renderer.render(it));
const nameCounts = {};
trackedNames.forEach(meta => {
+60 -22
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
this.addPlugin = function (pluginType, fnPlugin) {
MiscUtil.getOrSet(this._plugins, pluginType, []).push(fnPlugin);
@@ -309,21 +353,6 @@ globalThis.Renderer = function () {
};
// 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>"; };
/**
@@ -1431,19 +1460,26 @@ globalThis.Renderer = function () {
};
this._renderStatblock = function (entry, textStack, meta, options) {
this._renderPrefix(entry, textStack, meta, options);
const page = entry.prop || Renderer.tag.getPage(entry.tag);
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 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) {
this._renderPrefix(entry, textStack, meta, options);
this._renderDataHeader(textStack, entry.name, entry.style);
textStack[0] += `<tr>
<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>
</tr>`;
this._renderDataFooter(textStack);
@@ -1452,10 +1488,11 @@ globalThis.Renderer = function () {
return;
}
this._renderPrefix(entry, textStack, meta, options);
this._renderDataHeader(textStack, entry.displayName || entry.name, entry.style, {isCollapsed: entry.collapsed});
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()}">
<i>Loading ${entry.tag ? `${Renderer.get().render(asTag)}` : entry.displayName || entry.name}...</i>
<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 ${tag ? `${Renderer.get().render(asTag)}` : entry.displayName || entry.name}...</i>
<style onload="Renderer.events.handleLoad_inlineStatblock(this)"></style>
</td>
</tr>`;
@@ -10494,7 +10531,7 @@ Renderer.recipe = class {
? `{@b {@style Makes|small-caps}} ${ent._scaleFactor ? `${ent._scaleFactor}× ` : ""}${ent.makes}`
: null,
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,
entryMetasTime: Renderer.recipe._getEntryMetasTime(ent),
entryIngredients: {entries: ent._fullIngredients},
@@ -10680,6 +10717,7 @@ Renderer.recipe = class {
"tablespoon",
"teaspoon",
"wedge",
"fist",
];
static _UNITS_SINGLE_TO_PLURAL_ES = [
"dash",
+7 -3
View File
@@ -113,9 +113,11 @@ export class BrewUtil2Base {
isReloadRequired () { return this._isDirty; }
doLocationReload () {
if (typeof Hist !== "undefined") Hist.doPreLocationReload();
else window.location.hash = "";
doLocationReload ({isRetainHash = false} = {}) {
if (!isRetainHash) {
if (typeof Hist !== "undefined") Hist.doPreLocationReload();
else window.location.hash = "";
}
location.reload();
}
@@ -513,6 +515,8 @@ export class BrewUtil2Base {
pLoadPropIndex (urlRoot) { throw new Error("Unimplemented!"); }
/** @abstract */
pLoadMetaIndex (urlRoot) { throw new Error("Unimplemented!"); }
/** @abstract */
pLoadAdventureBookIdsIndex (urlRoot) { throw new Error("Unimplemented!"); }
async pGetCombinedIndexes () {
const urlRoot = await this.pGetCustomUrl();
+2
View File
@@ -89,6 +89,8 @@ export class BrewUtil2_ extends BrewUtil2Base {
pLoadMetaIndex (urlRoot) { return DataUtil.brew.pLoadMetaIndex(urlRoot); }
pLoadAdventureBookIdsIndex (urlRoot) { return DataUtil.brew.pLoadAdventureBookIdsIndex(urlRoot); }
/* -------------------------------------------- */
// region Editable
@@ -41,6 +41,8 @@ export class PrereleaseUtil_ extends BrewUtil2Base {
pLoadMetaIndex (urlRoot) { return DataUtil.prerelease.pLoadMetaIndex(urlRoot); }
pLoadAdventureBookIdsIndex (urlRoot) { return DataUtil.prerelease.pLoadAdventureBookIdsIndex(urlRoot); }
/* -------------------------------------------- */
// region Editable
+1 -1
View File
@@ -1987,7 +1987,7 @@ class DataLoader {
}
static async pCacheAndGetHash (page, hash, opts) {
const {source} = UrlUtil.autoDecodeHash(hash, {page});
const {source} = await UrlUtil.pAutoDecodeHash(hash, {page});
if (!source) {
if (opts.isRequired) throw new Error(`Could not find entity for page "${page}" with hash "${hash}"`);
return null;
+2
View File
@@ -1907,6 +1907,8 @@ PropOrder._VARIANTRULE = [
"type",
"entries",
"foundryImg",
];
PropOrder._RACE_SUBRACE = [
"page",
+1 -1
View File
@@ -965,7 +965,7 @@ class ListUiUtil {
let elePreviewWrp;
if (item.ele.children.length === 1) {
elePreviewWrp = e_({
ag: "div",
tag: "div",
clazz: "ve-hidden ve-flex",
children: [
e_({tag: "div", clazz: "ve-col-0-5"}),
+70 -1
View File
@@ -2,7 +2,7 @@
// in deployment, `IS_DEPLOYED = "<version number>";` should be set below.
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;
// for the roll20 script to set
globalThis.IS_VTT = false;
@@ -2812,7 +2812,24 @@ globalThis.UrlUtil = {
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
/**
* @param hash
* @param {?string} page
*/
autoDecodeHash (hash, {page = null} = {}) {
page ||= UrlUtil.getCurrentPage();
const parts = UrlUtil.decodeHash(hash.toLowerCase().trim());
@@ -2822,10 +2839,57 @@ globalThis.UrlUtil = {
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;
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) {
return Parser.stringToSlug(decodeURIComponent(hash)).replace(/_/g, "-");
},
@@ -3758,6 +3822,11 @@ class _DataUtilBrewHelper {
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) {
urlRoot = this._getCleanUrlRoot(urlRoot);
return `${urlRoot}${path}`;
+9 -9
View File
@@ -1,15 +1,15 @@
{
"name": "5etools",
"version": "1.209.1",
"version": "1.209.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "5etools",
"version": "1.209.1",
"version": "1.209.2",
"license": "MIT",
"devDependencies": {
"5etools-utils": "^0.12.19",
"5etools-utils": "^0.12.20",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"commander": "^12.0.0",
@@ -3926,9 +3926,9 @@
"dev": true
},
"node_modules/5etools-utils": {
"version": "0.12.19",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.19.tgz",
"integrity": "sha512-0VyiX585H93CxKshYX7sUq0mqTjmBUQSe0AgeCpD9MqCq3wTtQCs1nIFBxjb8zQR1DqNDUc+JiMLfmEe/XHGug==",
"version": "0.12.20",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.20.tgz",
"integrity": "sha512-PA5ZMMId+jvoUJvr19eNOq3skfTw2h57iHR4hO3YU2YDKOojtLB35vbasayZ7zJmoelDIqyAk/LF4j/lDpTZrg==",
"dev": true,
"dependencies": {
"ajv": "^8.12.0",
@@ -14589,9 +14589,9 @@
"dev": true
},
"5etools-utils": {
"version": "0.12.19",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.19.tgz",
"integrity": "sha512-0VyiX585H93CxKshYX7sUq0mqTjmBUQSe0AgeCpD9MqCq3wTtQCs1nIFBxjb8zQR1DqNDUc+JiMLfmEe/XHGug==",
"version": "0.12.20",
"resolved": "https://registry.npmjs.org/5etools-utils/-/5etools-utils-0.12.20.tgz",
"integrity": "sha512-PA5ZMMId+jvoUJvr19eNOq3skfTw2h57iHR4hO3YU2YDKOojtLB35vbasayZ7zJmoelDIqyAk/LF4j/lDpTZrg==",
"dev": true,
"requires": {
"ajv": "^8.12.0",
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "5etools",
"author": "TheGiddyLimit",
"version": "1.209.1",
"version": "1.209.2",
"license": "MIT",
"description": "A site dedicated to making playing games with your friends as easy as possible.",
"type": "module",
@@ -46,7 +46,7 @@
"url": "git+https://github.com/5etools-mirror-2/5etools-mirror-2.github.io.git"
},
"devDependencies": {
"5etools-utils": "^0.12.19",
"5etools-utils": "^0.12.20",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"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
+1 -1
View File
File diff suppressed because one or more lines are too long
+11
View File
@@ -2,6 +2,7 @@ import fs from "fs";
import path from "path";
import "../js/parser.js";
import "../js/utils.js";
import "../js/render.js";
import * as ut from "../node/util.js";
import {listFiles} from "../node/util.js";
@@ -41,6 +42,8 @@ class _TestTokenImages {
.forEach(file => {
ut.readJson(`./data/bestiary/${file}`).monster
.forEach(m => {
m.__prop = "monster";
const implicitTokenPath = `${this._PATH_BASE}/${m.source}/${Parser.nameToTokenName(m.name)}.${this._EXT}`;
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}`));
}
// 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
if (m.altArt) {
m.altArt