Add the Bond: 10 animal companions, with sheet support and rules
Companions were a single line on the Ranger's edge; now they are a real subsystem, rollable or pickable, with art, tokens and a place on the sheet. - New compendium "companions" (VANITY · Animal Companions): 10 beasts as full NPC actors with painted core artwork, droppable as tokens — wolf, hawk, owl, raven, hound, lynx, serpent, stag, great bat, ram. Each keeps the printed baseline (Grit 3, attacks at 3 dice) and differs in dodge (2-4d), senses and one distinctive trick, plus which companion action it favours. - New d10 roll table, "The Bond — Animal Companions". - Ranger sheet: the companion block is now a card — art, name, kind, attack, dodge, Grit (with a DOWNED flag), its trick, and one-click Help / Harry / Guard (each posting what it does to chat) plus Attack and Dodge rolls. "Roll the Bond" rolls the d10 and binds the beast straight onto the sheet. - template.json: companion gains kind, img, dodge, trick. - Rules updated in both places: §9a in the Draft 13 source (so the in-world journal carries it) and a new Bond section in the player rulebook, which is regenerated to HTML + PDF (39pp). - Version -> 0.9.6; README/FOUNDRY/system description updated (13 compendia). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.695783 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.696366 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.696541 173f1b000 Level-0 table #5: 2618 bytes OK
|
||||
2026/07/15-11:47:01.696627 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.696697 173f1b000 Manual compaction at level-0 from '!items!GOAakTqMDZSCDOdE' @ 72057594037927935 : 1 .. '!items!vHyUhdri4IKpq9CW' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:31.980954 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:31.982902 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:31.983101 173bcf000 Level-0 table #5: 2618 bytes OK
|
||||
2026/07/15-13:54:31.983209 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:31.983289 173bcf000 Manual compaction at level-0 from '!items!GOAakTqMDZSCDOdE' @ 72057594037927935 : 1 .. '!items!vHyUhdri4IKpq9CW' @ 0 : 0; will stop at (end)
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.788244 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.792649 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.792913 173f1b000 Level-0 table #5: 5226 bytes OK
|
||||
2026/07/15-11:47:01.793011 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.793107 173f1b000 Manual compaction at level-0 from '!actors!351sA95uALULY3Kv' @ 72057594037927935 : 1 .. '!actors!rPfEYHmSoprnQdrY' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.036496 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.036915 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.037137 173bcf000 Level-0 table #5: 5226 bytes OK
|
||||
2026/07/15-13:54:32.037231 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.037330 173bcf000 Manual compaction at level-0 from '!actors!351sA95uALULY3Kv' @ 72057594037927935 : 1 .. '!actors!rPfEYHmSoprnQdrY' @ 0 : 0; will stop at (end)
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
MANIFEST-000002
|
||||
@@ -0,0 +1,5 @@
|
||||
2026/07/15-13:54:32.041086 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.041857 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.042052 173bcf000 Level-0 table #5: 4974 bytes OK
|
||||
2026/07/15-13:54:32.042132 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.042191 173bcf000 Manual compaction at level-0 from '!actors!1FGl7v1mODKvN9fy' @ 72057594037927935 : 1 .. '!actors!yEfKBqSa4GmIgOpx' @ 0 : 0; will stop at (end)
|
||||
Binary file not shown.
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.770669 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.771135 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.771347 173f1b000 Level-0 table #5: 5417 bytes OK
|
||||
2026/07/15-11:47:01.771446 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.771504 173f1b000 Manual compaction at level-0 from '!items!21hRQJtWGk5fDQlo' @ 72057594037927935 : 1 .. '!items!zMphj752klzDFU3Q' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.027062 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.028202 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.028447 173bcf000 Level-0 table #5: 5417 bytes OK
|
||||
2026/07/15-13:54:32.028559 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.028646 173bcf000 Manual compaction at level-0 from '!items!21hRQJtWGk5fDQlo' @ 72057594037927935 : 1 .. '!items!zMphj752klzDFU3Q' @ 0 : 0; will stop at (end)
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.714562 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.715170 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.715419 173f1b000 Level-0 table #5: 9529 bytes OK
|
||||
2026/07/15-11:47:01.715518 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.715585 173f1b000 Manual compaction at level-0 from '!items!27LcrYsIEEF3KYHA' @ 72057594037927935 : 1 .. '!items!yo48EzCVEwNdpkXF' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:31.993791 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:31.994306 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:31.994579 173bcf000 Level-0 table #5: 9529 bytes OK
|
||||
2026/07/15-13:54:31.994675 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:31.994726 173bcf000 Manual compaction at level-0 from '!items!27LcrYsIEEF3KYHA' @ 72057594037927935 : 1 .. '!items!yo48EzCVEwNdpkXF' @ 0 : 0; will stop at (end)
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.833917 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.834447 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.834747 173f1b000 Level-0 table #5: 10149 bytes OK
|
||||
2026/07/15-11:47:01.834842 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.834916 173f1b000 Manual compaction at level-0 from '!journal!JJEgDJvtWBnFbyZA' @ 72057594037927935 : 1 .. '!journal.pages!kBclT2QiwEQmOsX1.HSVD8c7BenQjb45v' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.071464 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.071868 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.072129 173bcf000 Level-0 table #5: 10149 bytes OK
|
||||
2026/07/15-13:54:32.072216 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.072285 173bcf000 Manual compaction at level-0 from '!journal!JJEgDJvtWBnFbyZA' @ 72057594037927935 : 1 .. '!journal.pages!kBclT2QiwEQmOsX1.HSVD8c7BenQjb45v' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1 +1 @@
|
||||
MANIFEST-000006
|
||||
MANIFEST-000002
|
||||
|
||||
+5
-3
@@ -1,3 +1,5 @@
|
||||
2026/07/15-11:47:22.058812 172c77000 Recovering log #4
|
||||
2026/07/15-11:47:22.059752 172c77000 Delete type=0 #4
|
||||
2026/07/15-11:47:22.059797 172c77000 Delete type=3 #2
|
||||
2026/07/15-13:54:32.058937 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.060571 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.061856 173bcf000 Level-0 table #5: 120604 bytes OK
|
||||
2026/07/15-13:54:32.061959 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.062074 173bcf000 Manual compaction at level-0 from '!actors!3Kjqu9ibyf8TjSrk' @ 72057594037927935 : 1 .. '!actors.items!zR0OZzFsVQLTSlBM.uc6AKpWyZeREAX2M' @ 0 : 0; will stop at (end)
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
2026/07/15-11:47:01.820316 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.821872 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.824245 173f1b000 Level-0 table #5: 120604 bytes OK
|
||||
2026/07/15-11:47:01.824415 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.824651 173f1b000 Manual compaction at level-0 from '!actors!3Kjqu9ibyf8TjSrk' @ 72057594037927935 : 1 .. '!actors.items!zR0OZzFsVQLTSlBM.uc6AKpWyZeREAX2M' @ 0 : 0; will stop at (end)
|
||||
Binary file not shown.
Binary file not shown.
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.829347 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.830098 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.830873 173f1b000 Level-0 table #5: 63868 bytes OK
|
||||
2026/07/15-11:47:01.831105 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.831206 173f1b000 Manual compaction at level-0 from '!journal!9PRuE5Q81BnO22fH' @ 72057594037927935 : 1 .. '!journal.pages!9PRuE5Q81BnO22fH.qCt9jGDZePPN5S6G' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.066661 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.067500 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.068353 173bcf000 Level-0 table #5: 63868 bytes OK
|
||||
2026/07/15-13:54:32.068488 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.068594 173bcf000 Manual compaction at level-0 from '!journal!9PRuE5Q81BnO22fH' @ 72057594037927935 : 1 .. '!journal.pages!9PRuE5Q81BnO22fH.qCt9jGDZePPN5S6G' @ 0 : 0; will stop at (end)
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.760698 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.761279 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.761565 173f1b000 Level-0 table #5: 11569 bytes OK
|
||||
2026/07/15-11:47:01.761661 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.761720 173f1b000 Manual compaction at level-0 from '!items!13KOIsQOsgYqdVEX' @ 72057594037927935 : 1 .. '!items!zywiqamhRlcCgLJY' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.019121 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.019649 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.019916 173bcf000 Level-0 table #5: 11569 bytes OK
|
||||
2026/07/15-13:54:32.020010 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.020081 173bcf000 Manual compaction at level-0 from '!items!13KOIsQOsgYqdVEX' @ 72057594037927935 : 1 .. '!items!zywiqamhRlcCgLJY' @ 0 : 0; will stop at (end)
|
||||
|
||||
Binary file not shown.
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.839105 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.839563 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.839866 173f1b000 Level-0 table #5: 12927 bytes OK
|
||||
2026/07/15-11:47:01.839954 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.840012 173f1b000 Manual compaction at level-0 from '!tables!JEOcxCxjK6S6pVEZ' @ 72057594037927935 : 1 .. '!tables.results!rurx5ZPcPObdbNDD.bWBU56ZArkeat9DO' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.076783 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.077935 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.078454 173bcf000 Level-0 table #5: 15588 bytes OK
|
||||
2026/07/15-13:54:32.078562 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.078643 173bcf000 Manual compaction at level-0 from '!tables!JEOcxCxjK6S6pVEZ' @ 72057594037927935 : 1 .. '!tables.results!rurx5ZPcPObdbNDD.bWBU56ZArkeat9DO' @ 0 : 0; will stop at (end)
|
||||
|
||||
Binary file not shown.
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.747896 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.748609 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.748977 173f1b000 Level-0 table #5: 27619 bytes OK
|
||||
2026/07/15-11:47:01.749069 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.749125 173f1b000 Manual compaction at level-0 from '!items!0NDwQbhQQNeCCEco' @ 72057594037927935 : 1 .. '!items!yxds1AZFNXBpERdk' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.011854 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.012716 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.013114 173bcf000 Level-0 table #5: 27619 bytes OK
|
||||
2026/07/15-13:54:32.013198 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.013256 173bcf000 Manual compaction at level-0 from '!items!0NDwQbhQQNeCCEco' @ 72057594037927935 : 1 .. '!items!yxds1AZFNXBpERdk' @ 0 : 0; will stop at (end)
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.775377 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.775829 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.776000 173f1b000 Level-0 table #5: 1461 bytes OK
|
||||
2026/07/15-11:47:01.776104 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.776160 173f1b000 Manual compaction at level-0 from '!items!7kO3VzfFPt8QRVXT' @ 72057594037927935 : 1 .. '!items!s50CZgFUaS5puSOp' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:32.031270 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:32.031615 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:32.031760 173bcf000 Level-0 table #5: 1461 bytes OK
|
||||
2026/07/15-13:54:32.031837 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:32.031883 173bcf000 Manual compaction at level-0 from '!items!7kO3VzfFPt8QRVXT' @ 72057594037927935 : 1 .. '!items!s50CZgFUaS5puSOp' @ 0 : 0; will stop at (end)
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
2026/07/15-11:47:01.686516 173683000 Delete type=3 #1
|
||||
2026/07/15-11:47:01.688737 173f1b000 Level-0 table #5: started
|
||||
2026/07/15-11:47:01.688968 173f1b000 Level-0 table #5: 7415 bytes OK
|
||||
2026/07/15-11:47:01.689074 173f1b000 Delete type=0 #3
|
||||
2026/07/15-11:47:01.689142 173f1b000 Manual compaction at level-0 from '!items!0d6FZCtl78jEPkof' @ 72057594037927935 : 1 .. '!items!v3IghflF7cI2FzbU' @ 0 : 0; will stop at (end)
|
||||
2026/07/15-13:54:31.973874 173337000 Delete type=3 #1
|
||||
2026/07/15-13:54:31.975860 173bcf000 Level-0 table #5: started
|
||||
2026/07/15-13:54:31.976110 173bcf000 Level-0 table #5: 7415 bytes OK
|
||||
2026/07/15-13:54:31.976203 173bcf000 Delete type=0 #3
|
||||
2026/07/15-13:54:31.976263 173bcf000 Manual compaction at level-0 from '!items!0d6FZCtl78jEPkof' @ 72057594037927935 : 1 .. '!items!v3IghflF7cI2FzbU' @ 0 : 0; will stop at (end)
|
||||
|
||||
+37
-4
@@ -425,10 +425,43 @@
|
||||
|
||||
/* ---------------- Companion ---------------- */
|
||||
|
||||
.vanity-sheet .companion-grid { display: grid; grid-template-columns: 2fr 1fr 1.4fr auto; gap: 10px; align-items: end; }
|
||||
.vanity-sheet .companion-grid .wide { grid-column: 1 / -1; }
|
||||
.vanity-sheet .companion-grid input[type="number"] { width: 44px; text-align: center; }
|
||||
.vanity-sheet .companion-roll { padding: 5px 12px; }
|
||||
.vanity-sheet .companion-card { display: flex; gap: 12px; align-items: flex-start; }
|
||||
.vanity-sheet .cmp-art {
|
||||
flex: none; width: 92px; height: 92px; border-radius: 6px; overflow: hidden;
|
||||
border: 1.5px solid var(--v-gold); background: var(--v-paper2);
|
||||
}
|
||||
.vanity-sheet .cmp-art img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.vanity-sheet .cmp-art-empty {
|
||||
width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
|
||||
color: var(--v-line); font-size: 30px;
|
||||
}
|
||||
.vanity-sheet .cmp-body { flex: 1; display: flex; flex-direction: column; gap: 7px; min-width: 0; }
|
||||
.vanity-sheet .cmp-line { display: flex; gap: 10px; align-items: end; }
|
||||
.vanity-sheet .cmp-line .grow { flex: 1; min-width: 0; }
|
||||
.vanity-sheet .companion-card input[type="number"] { width: 42px; text-align: center; }
|
||||
.vanity-sheet .companion-card .sl { color: var(--v-ink2); padding: 0 2px; }
|
||||
.vanity-sheet .companion-card .wide { width: 100%; }
|
||||
.vanity-sheet .cmp-downed {
|
||||
font-family: "Cinzel", Georgia, serif; font-size: 9px; letter-spacing: .1em;
|
||||
color: #fff; background: #8a2f2f; border-radius: 8px; padding: 2px 8px; align-self: center;
|
||||
}
|
||||
.vanity-sheet .cmp-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
|
||||
.vanity-sheet .cmp-actions .cap { width: 100%; margin-bottom: 1px; }
|
||||
.vanity-sheet .cmp-act, .vanity-sheet .cmp-roll {
|
||||
padding: 4px 10px; font-size: 11px; border: 1.3px solid var(--v-line);
|
||||
border-radius: 12px; background: var(--v-paper2); cursor: pointer; white-space: nowrap;
|
||||
}
|
||||
.vanity-sheet .cmp-act b { color: var(--v-teal); font-weight: 600; }
|
||||
.vanity-sheet .cmp-act:hover, .vanity-sheet .cmp-roll:hover {
|
||||
border-color: var(--v-gold); background: rgba(176,135,47,.10);
|
||||
}
|
||||
.vanity-sheet .cmp-roll { border-color: var(--v-teal); color: var(--v-teal-d); }
|
||||
.vanity-sheet .cmp-bond {
|
||||
float: right; margin: -2px 0 0; padding: 2px 9px; font-size: 10px;
|
||||
border: 1.2px solid var(--v-gold); border-radius: 10px; background: transparent;
|
||||
color: var(--v-paper); cursor: pointer;
|
||||
}
|
||||
.vanity-sheet .cmp-bond:hover { background: rgba(255,255,255,.14); }
|
||||
|
||||
/* ---------------- NPC sheet ---------------- */
|
||||
|
||||
|
||||
+9
-2
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "vanity",
|
||||
"title": "VANITY — the Vanity Role-Playing Game",
|
||||
"description": "A generic d6 RPG where ego is a resource and the universe keeps score. Pool = Attribute + Knack + situation; 5–6 = a Success; fail with two 1s = a Stumble that banks a Bane of Karma (Draft 13). No hit points — Grit. Vanity, Karma and Glory drive play. Includes the full Draft 13 rulebook, all spells and gear, the starter bestiary, a 100-item Treasures & Relics library, twenty ready-to-play characters (each with cameo art, token and backstory) and the one-shot adventure The Vain Crown.",
|
||||
"version": "0.9.5",
|
||||
"description": "A generic d6 RPG where ego is a resource and the universe keeps score. Pool = Attribute + Knack + situation; 5–6 = a Success; fail with two 1s = a Stumble that banks a Bane of Karma (Draft 13). No hit points — Grit. Vanity, Karma and Glory drive play. Includes the full Draft 13 rulebook, all spells and gear, the starter bestiary, a 100-item Treasures & Relics library, twenty ready-to-play characters (each with cameo art, token and backstory), ten animal companions with the d10 Bond table and the one-shot adventure The Vain Crown.",
|
||||
"version": "0.9.6",
|
||||
"compatibility": {
|
||||
"minimum": 13,
|
||||
"verified": "14.364"
|
||||
@@ -90,6 +90,13 @@
|
||||
"type": "Actor",
|
||||
"system": "vanity"
|
||||
},
|
||||
{
|
||||
"name": "companions",
|
||||
"label": "VANITY · Animal Companions",
|
||||
"path": "packs/companions",
|
||||
"type": "Actor",
|
||||
"system": "vanity"
|
||||
},
|
||||
{
|
||||
"name": "pregens",
|
||||
"label": "VANITY · Ready-to-Play Characters",
|
||||
|
||||
+100
-27
@@ -1,27 +1,65 @@
|
||||
{
|
||||
"Actor": {
|
||||
"types": ["character", "npc"],
|
||||
"types": [
|
||||
"character",
|
||||
"npc"
|
||||
],
|
||||
"character": {
|
||||
"attributes": {
|
||||
"brawn": { "value": 2 },
|
||||
"flair": { "value": 2 },
|
||||
"wits": { "value": 2 },
|
||||
"poise": { "value": 2 }
|
||||
"brawn": {
|
||||
"value": 2
|
||||
},
|
||||
"flair": {
|
||||
"value": 2
|
||||
},
|
||||
"wits": {
|
||||
"value": 2
|
||||
},
|
||||
"poise": {
|
||||
"value": 2
|
||||
}
|
||||
},
|
||||
"grit": {
|
||||
"value": 8,
|
||||
"max": 8
|
||||
},
|
||||
"vanity": {
|
||||
"value": 3
|
||||
},
|
||||
"karma": {
|
||||
"value": 0
|
||||
},
|
||||
"glory": {
|
||||
"value": 0
|
||||
},
|
||||
"grit": { "value": 8, "max": 8 },
|
||||
"vanity": { "value": 3 },
|
||||
"karma": { "value": 0 },
|
||||
"glory": { "value": 0 },
|
||||
"knacks": {
|
||||
"fight": { "rank": 0 },
|
||||
"shoot": { "rank": 0 },
|
||||
"athletics": { "rank": 0 },
|
||||
"stealth": { "rank": 0 },
|
||||
"notice": { "rank": 0 },
|
||||
"lore": { "rank": 0 },
|
||||
"survive": { "rank": 0 },
|
||||
"charm": { "rank": 0 },
|
||||
"command": { "rank": 0 }
|
||||
"fight": {
|
||||
"rank": 0
|
||||
},
|
||||
"shoot": {
|
||||
"rank": 0
|
||||
},
|
||||
"athletics": {
|
||||
"rank": 0
|
||||
},
|
||||
"stealth": {
|
||||
"rank": 0
|
||||
},
|
||||
"notice": {
|
||||
"rank": 0
|
||||
},
|
||||
"lore": {
|
||||
"rank": 0
|
||||
},
|
||||
"survive": {
|
||||
"rank": 0
|
||||
},
|
||||
"charm": {
|
||||
"rank": 0
|
||||
},
|
||||
"command": {
|
||||
"rank": 0
|
||||
}
|
||||
},
|
||||
"locked": {
|
||||
"name": "",
|
||||
@@ -32,12 +70,23 @@
|
||||
"class": "",
|
||||
"concept": "",
|
||||
"fightingStyle": "",
|
||||
"coin": { "cp": 0, "sp": 0, "gp": 15 }
|
||||
"coin": {
|
||||
"cp": 0,
|
||||
"sp": 0,
|
||||
"gp": 15
|
||||
}
|
||||
},
|
||||
"companion": {
|
||||
"name": "",
|
||||
"grit": { "value": 3, "max": 3 },
|
||||
"kind": "",
|
||||
"img": "",
|
||||
"grit": {
|
||||
"value": 3,
|
||||
"max": 3
|
||||
},
|
||||
"pool": 3,
|
||||
"dodge": 3,
|
||||
"trick": "",
|
||||
"notes": ""
|
||||
},
|
||||
"notes": "",
|
||||
@@ -45,18 +94,42 @@
|
||||
},
|
||||
"npc": {
|
||||
"category": "mook",
|
||||
"attack1": { "name": "Attack", "pool": 3, "note": "" },
|
||||
"attack2": { "name": "", "pool": 0, "note": "" },
|
||||
"defence": { "pool": 2, "note": "" },
|
||||
"grit": { "value": 1, "max": 1 },
|
||||
"resolve": { "value": 0, "max": 0 },
|
||||
"attack1": {
|
||||
"name": "Attack",
|
||||
"pool": 3,
|
||||
"note": ""
|
||||
},
|
||||
"attack2": {
|
||||
"name": "",
|
||||
"pool": 0,
|
||||
"note": ""
|
||||
},
|
||||
"defence": {
|
||||
"pool": 2,
|
||||
"note": ""
|
||||
},
|
||||
"grit": {
|
||||
"value": 1,
|
||||
"max": 1
|
||||
},
|
||||
"resolve": {
|
||||
"value": 0,
|
||||
"max": 0
|
||||
},
|
||||
"nerve": 2,
|
||||
"trick": "",
|
||||
"notes": ""
|
||||
}
|
||||
},
|
||||
"Item": {
|
||||
"types": ["weapon", "armour", "gear", "spell", "edge", "vice"],
|
||||
"types": [
|
||||
"weapon",
|
||||
"armour",
|
||||
"gear",
|
||||
"spell",
|
||||
"edge",
|
||||
"vice"
|
||||
],
|
||||
"weapon": {
|
||||
"dice": 0,
|
||||
"hands": 1,
|
||||
@@ -98,4 +171,4 @@
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,13 +273,43 @@
|
||||
{{!-- ============ TAB: STORY ============ --}}
|
||||
<div class="tab" data-tab="story" data-group="primary">
|
||||
<div class="sect">
|
||||
<div class="bar">Companion</div>
|
||||
<div class="body companion-grid">
|
||||
<label class="f"><span class="cap">Name</span><input type="text" name="system.companion.name" value="{{sys.companion.name}}" placeholder="Ash the hawk…" /></label>
|
||||
<label class="f"><span class="cap">Pool</span><input type="number" name="system.companion.pool" value="{{sys.companion.pool}}" /></label>
|
||||
<label class="f"><span class="cap">Grit</span><input type="number" name="system.companion.grit.value" value="{{sys.companion.grit.value}}" /> / <input type="number" name="system.companion.grit.max" value="{{sys.companion.grit.max}}" /></label>
|
||||
<button type="button" class="companion-roll" title="Roll the companion's pool"><i class="fa-solid fa-paw"></i> Roll</button>
|
||||
<label class="f wide"><span class="cap">Notes (Help · Harry · Guard — Downed at 0 Grit, not dead)</span><input type="text" name="system.companion.notes" value="{{sys.companion.notes}}" /></label>
|
||||
<div class="bar">Companion — the Bond
|
||||
<button type="button" class="cmp-bond" title="Roll d10 on The Bond, or pick from the Animal Companions compendium"><i class="fa-solid fa-dice-d20"></i> Roll the Bond</button>
|
||||
</div>
|
||||
<div class="body companion-card">
|
||||
<div class="cmp-art">
|
||||
{{#if sys.companion.img}}<img src="{{sys.companion.img}}" alt="{{sys.companion.name}}" />
|
||||
{{else}}<div class="cmp-art-empty"><i class="fa-solid fa-paw"></i></div>{{/if}}
|
||||
</div>
|
||||
<div class="cmp-body">
|
||||
<div class="cmp-line">
|
||||
<label class="f grow"><span class="cap">Name</span>
|
||||
<input type="text" name="system.companion.name" value="{{sys.companion.name}}" placeholder="Ash…" /></label>
|
||||
<label class="f grow"><span class="cap">Kind</span>
|
||||
<input type="text" name="system.companion.kind" value="{{sys.companion.kind}}" placeholder="hawk" /></label>
|
||||
</div>
|
||||
<div class="cmp-line">
|
||||
<label class="f"><span class="cap">Attack</span>
|
||||
<input type="number" name="system.companion.pool" value="{{sys.companion.pool}}" /></label>
|
||||
<label class="f"><span class="cap">Dodge</span>
|
||||
<input type="number" name="system.companion.dodge" value="{{sys.companion.dodge}}" /></label>
|
||||
<label class="f"><span class="cap">Grit</span>
|
||||
<input type="number" name="system.companion.grit.value" value="{{sys.companion.grit.value}}" />
|
||||
<span class="sl">/</span>
|
||||
<input type="number" name="system.companion.grit.max" value="{{sys.companion.grit.max}}" /></label>
|
||||
{{#if companionDowned}}<span class="cmp-downed">DOWNED — not dead</span>{{/if}}
|
||||
</div>
|
||||
<label class="f wide"><span class="cap">Trick</span>
|
||||
<input type="text" name="system.companion.trick" value="{{sys.companion.trick}}" placeholder="its one distinctive knack…" /></label>
|
||||
<div class="cmp-actions">
|
||||
<span class="cap">Each round, one action</span>
|
||||
<button type="button" class="cmp-act" data-act="help" title="It flanks, flushes or calls the shot: you gain +1 die">Help <b>+1 die</b></button>
|
||||
<button type="button" class="cmp-act" data-act="harry" title="It worries a foe: −1 die, and you may treat it as flanked">Harry <b>−1 die</b></button>
|
||||
<button type="button" class="cmp-act" data-act="guard" title="Reaction: it intercepts one hit meant for you">Guard <b>reaction</b></button>
|
||||
<button type="button" class="cmp-roll" data-roll="attack" title="Roll the companion's attack pool"><i class="fa-solid fa-paw"></i> Attack</button>
|
||||
<button type="button" class="cmp-roll" data-roll="dodge" title="Roll the companion's dodge"><i class="fa-solid fa-feather"></i> Dodge</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect">
|
||||
|
||||
+66
-5
@@ -633,6 +633,8 @@ class VanityActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
items: ctx.conditions.filter(c => c.group === g)
|
||||
}));
|
||||
ctx.activeConditions = ctx.conditions.filter(c => c.active);
|
||||
const cmp = this.actor.system.companion;
|
||||
ctx.companionDowned = !!cmp?.name && (cmp.grit?.value ?? 3) <= 0;
|
||||
ctx.focusCond = this._focusCond
|
||||
? ctx.conditions.find(c => c.id === this._focusCond) ?? null
|
||||
: null;
|
||||
@@ -881,11 +883,69 @@ class VanityActorSheet extends foundry.appv1.sheets.ActorSheet {
|
||||
this.render(false);
|
||||
});
|
||||
|
||||
// Companion roll
|
||||
html.find(".companion-roll").click(() => {
|
||||
const c = actor.system.companion;
|
||||
if (!c?.name) return;
|
||||
promptAndRoll(actor, { title: `${c.name} (companion)`, base: c.pool || 3, baseLabel: `Companion pool ${c.pool || 3}` });
|
||||
// Companion — attack / dodge
|
||||
html.find(".cmp-roll").click(ev => {
|
||||
const c = actor.system.companion ?? {};
|
||||
const name = c.name || "The companion";
|
||||
if (ev.currentTarget.dataset.roll === "dodge") {
|
||||
return promptAndRoll(actor, { title: `${name} — dodges`, base: c.dodge || 3,
|
||||
baseLabel: `Companion dodge ${c.dodge || 3}` });
|
||||
}
|
||||
return promptAndRoll(actor, { title: `${name} — attacks`, base: c.pool || 3,
|
||||
baseLabel: `Companion pool ${c.pool || 3}`, options: { context: "attack" } });
|
||||
});
|
||||
|
||||
// Companion — Help · Harry · Guard (one action a round)
|
||||
html.find(".cmp-act").click(async ev => {
|
||||
const c = actor.system.companion ?? {};
|
||||
const name = c.name || "The companion";
|
||||
const kind = c.kind ? ` the ${c.kind}` : "";
|
||||
const ACTS = {
|
||||
help: ["Help", "+1 die",
|
||||
`${name}${kind} flanks, flushes or calls the shot — <b>${actor.name} gains +1 die</b> on their next roll this round.`],
|
||||
harry: ["Harry", "−1 die",
|
||||
`${name}${kind} worries the foe — that foe takes <b>−1 die</b>, and ${actor.name} may treat it as <b>flanked</b> for Ambush-style shots.`],
|
||||
guard: ["Guard", "reaction",
|
||||
`${name}${kind} throws itself in the way — it <b>intercepts one hit</b> meant for ${actor.name}. That is the companion's reaction this round.`],
|
||||
};
|
||||
const [label, tag, text] = ACTS[ev.currentTarget.dataset.act] ?? [];
|
||||
if (!label) return;
|
||||
await ChatMessage.create({
|
||||
speaker: ChatMessage.getSpeaker({ actor }),
|
||||
content: `<div class="vanity-roll vanity-companion-card">
|
||||
<header><span class="vanity-roll-label">🐾 ${label} — ${tag}</span>
|
||||
<span class="vanity-roll-sub">${name}${kind} · the companion's action this round</span></header>
|
||||
<p>${text}</p>
|
||||
${c.trick ? `<p class="cmp-trick"><b>Trick:</b> ${c.trick}</p>` : ""}
|
||||
</div>`
|
||||
});
|
||||
});
|
||||
|
||||
// Companion — roll the Bond (d10) and bind the beast to the sheet
|
||||
html.find(".cmp-bond").click(async () => {
|
||||
const table = await getVanityTable(BOND_TABLE_NAME);
|
||||
if (!table) return ui.notifications.error(`"${BOND_TABLE_NAME}" not found in world or compendium.`);
|
||||
const roll = new Roll("1d10");
|
||||
await roll.evaluate();
|
||||
lockVanityDice(roll);
|
||||
if (game.dice3d) await game.dice3d.showForRoll(roll, game.user, true).catch(() => {});
|
||||
const results = table.getResultsForRoll(roll.total);
|
||||
await table.toMessage(results, { roll });
|
||||
|
||||
// Bind it: pull the matching beast from the companions compendium.
|
||||
const kind = (results?.[0]?.name ?? "").split("—")[0].trim();
|
||||
const beast = (await packDocs("companions")).find(d => d.name === `${kind} (companion)`);
|
||||
if (!beast) return;
|
||||
await actor.update({
|
||||
"system.companion.kind": kind.toLowerCase(),
|
||||
"system.companion.img": beast.img,
|
||||
"system.companion.pool": beast.system.attack1?.pool ?? 3,
|
||||
"system.companion.dodge": beast.system.defence?.pool ?? 3,
|
||||
"system.companion.trick": beast.system.trick ?? "",
|
||||
"system.companion.grit.value": beast.system.grit?.max ?? 3,
|
||||
"system.companion.grit.max": beast.system.grit?.max ?? 3,
|
||||
});
|
||||
ui.notifications.info(`The Bond: a ${kind.toLowerCase()}. Name it — the table will be shouting that name soon enough.`);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1019,6 +1079,7 @@ Hooks.on("diceSoNiceRollStart", (messageId, context) => {
|
||||
|
||||
const TWIST_TABLE_NAME = "Twist the Knife — spending Banes (d66)";
|
||||
const RECKONING_TABLE_NAME = "THE RECKONING — the tab reads six (2d6)";
|
||||
const BOND_TABLE_NAME = "The Bond — Animal Companions (d10)";
|
||||
|
||||
async function getVanityTable(name) {
|
||||
let table = game.tables.getName(name);
|
||||
|
||||
Reference in New Issue
Block a user