diff --git a/.impeccable/hook.cache.json b/.impeccable/hook.cache.json new file mode 100644 index 0000000..0555d36 --- /dev/null +++ b/.impeccable/hook.cache.json @@ -0,0 +1 @@ +{"version":1,"sessions":{"2ead103c-b952-47dc-aafe-6f914fbfd398":{"updatedAt":1785224728489,"files":{"/Users/timevans/Git/VRPG/foundry/vanity/styles/vanity.css":{"editCount":4,"findings":["side-tab:627","side-tab:1012","side-tab:632","side-tab:1017","side-tab:639","side-tab:1024","side-tab:672","side-tab:384","side-tab:1057"]}}}}} \ No newline at end of file diff --git a/assets/conditions/asleep.png b/assets/conditions/asleep.png index d2a339f..0133d90 100644 Binary files a/assets/conditions/asleep.png and b/assets/conditions/asleep.png differ diff --git a/assets/conditions/asleep.svg b/assets/conditions/asleep.svg index 0eea850..3d82330 100644 --- a/assets/conditions/asleep.svg +++ b/assets/conditions/asleep.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/blessed.png b/assets/conditions/blessed.png index 360dc6e..d5641aa 100644 Binary files a/assets/conditions/blessed.png and b/assets/conditions/blessed.png differ diff --git a/assets/conditions/blessed.svg b/assets/conditions/blessed.svg index a5bc442..a3e52a8 100644 --- a/assets/conditions/blessed.svg +++ b/assets/conditions/blessed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/blurred.png b/assets/conditions/blurred.png index 5195501..5e93121 100644 Binary files a/assets/conditions/blurred.png and b/assets/conditions/blurred.png differ diff --git a/assets/conditions/blurred.svg b/assets/conditions/blurred.svg index 624d001..d53c967 100644 --- a/assets/conditions/blurred.svg +++ b/assets/conditions/blurred.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/charmed.png b/assets/conditions/charmed.png index 7faa3cf..8a4ae40 100644 Binary files a/assets/conditions/charmed.png and b/assets/conditions/charmed.png differ diff --git a/assets/conditions/charmed.svg b/assets/conditions/charmed.svg index d224958..f3c952e 100644 --- a/assets/conditions/charmed.svg +++ b/assets/conditions/charmed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/cursed.png b/assets/conditions/cursed.png index 18063e4..3871577 100644 Binary files a/assets/conditions/cursed.png and b/assets/conditions/cursed.png differ diff --git a/assets/conditions/cursed.svg b/assets/conditions/cursed.svg index 90dcadc..c4da480 100644 --- a/assets/conditions/cursed.svg +++ b/assets/conditions/cursed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/downed.png b/assets/conditions/downed.png index c5fdf8c..d4df3cd 100644 Binary files a/assets/conditions/downed.png and b/assets/conditions/downed.png differ diff --git a/assets/conditions/downed.svg b/assets/conditions/downed.svg index 4fe4eb0..0716e81 100644 --- a/assets/conditions/downed.svg +++ b/assets/conditions/downed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/encumbered.png b/assets/conditions/encumbered.png index c473204..4e18f9f 100644 Binary files a/assets/conditions/encumbered.png and b/assets/conditions/encumbered.png differ diff --git a/assets/conditions/encumbered.svg b/assets/conditions/encumbered.svg index c22a6b0..871142a 100644 --- a/assets/conditions/encumbered.svg +++ b/assets/conditions/encumbered.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/held.png b/assets/conditions/held.png index e984643..c77ce16 100644 Binary files a/assets/conditions/held.png and b/assets/conditions/held.png differ diff --git a/assets/conditions/held.svg b/assets/conditions/held.svg index 8444197..b61f902 100644 --- a/assets/conditions/held.svg +++ b/assets/conditions/held.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/marked.png b/assets/conditions/marked.png index 48df631..c1cabb5 100644 Binary files a/assets/conditions/marked.png and b/assets/conditions/marked.png differ diff --git a/assets/conditions/marked.svg b/assets/conditions/marked.svg index 003677e..a9e32f8 100644 --- a/assets/conditions/marked.svg +++ b/assets/conditions/marked.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/off-balance.png b/assets/conditions/off-balance.png index 0cf50ce..1d38be1 100644 Binary files a/assets/conditions/off-balance.png and b/assets/conditions/off-balance.png differ diff --git a/assets/conditions/off-balance.svg b/assets/conditions/off-balance.svg index 767e650..37e97ed 100644 --- a/assets/conditions/off-balance.svg +++ b/assets/conditions/off-balance.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/prone.png b/assets/conditions/prone.png index d10f636..0172cd1 100644 Binary files a/assets/conditions/prone.png and b/assets/conditions/prone.png differ diff --git a/assets/conditions/prone.svg b/assets/conditions/prone.svg index 2eb86ae..3a4f6f8 100644 --- a/assets/conditions/prone.svg +++ b/assets/conditions/prone.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/rattled.png b/assets/conditions/rattled.png index a18a4c9..fca0265 100644 Binary files a/assets/conditions/rattled.png and b/assets/conditions/rattled.png differ diff --git a/assets/conditions/rattled.svg b/assets/conditions/rattled.svg index 0b44c26..eed5357 100644 --- a/assets/conditions/rattled.svg +++ b/assets/conditions/rattled.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/slowed.png b/assets/conditions/slowed.png index e4fd924..640fa8c 100644 Binary files a/assets/conditions/slowed.png and b/assets/conditions/slowed.png differ diff --git a/assets/conditions/slowed.svg b/assets/conditions/slowed.svg index 05f44cf..2890c94 100644 --- a/assets/conditions/slowed.svg +++ b/assets/conditions/slowed.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/spent.png b/assets/conditions/spent.png index ffece11..fda5b93 100644 Binary files a/assets/conditions/spent.png and b/assets/conditions/spent.png differ diff --git a/assets/conditions/spent.svg b/assets/conditions/spent.svg index 340aaa1..340cd76 100644 --- a/assets/conditions/spent.svg +++ b/assets/conditions/spent.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/taken-out.png b/assets/conditions/taken-out.png index 2dd0f03..0e1d0be 100644 Binary files a/assets/conditions/taken-out.png and b/assets/conditions/taken-out.png differ diff --git a/assets/conditions/taken-out.svg b/assets/conditions/taken-out.svg index abf52ec..af11ae1 100644 --- a/assets/conditions/taken-out.svg +++ b/assets/conditions/taken-out.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/warded.png b/assets/conditions/warded.png index 0f61b70..055b90c 100644 Binary files a/assets/conditions/warded.png and b/assets/conditions/warded.png differ diff --git a/assets/conditions/warded.svg b/assets/conditions/warded.svg index b401890..4a9edba 100644 --- a/assets/conditions/warded.svg +++ b/assets/conditions/warded.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/conditions/wounded.png b/assets/conditions/wounded.png index 011945d..aceb4da 100644 Binary files a/assets/conditions/wounded.png and b/assets/conditions/wounded.png differ diff --git a/assets/conditions/wounded.svg b/assets/conditions/wounded.svg index e9a9a97..09f4427 100644 --- a/assets/conditions/wounded.svg +++ b/assets/conditions/wounded.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/conditions.mjs b/conditions.mjs index d8f3b03..8e35d3c 100644 --- a/conditions.mjs +++ b/conditions.mjs @@ -44,9 +44,9 @@ export const VANITY_CONDITIONS = [ "id": "marked", "name": "Marked", "group": "combat", - "short": "Your next Stumble banks 2 Banes", - "rules": "Something patient has written your name. Your next Stumble banks 2 Banes instead of 1 — the GM will tell you when it lands.", - "ends": "Your next Stumble spends it" + "short": "The next Bane you bank counts as 2", + "rules": "Something patient has written your name. The next Bane you bank counts as 2 instead of 1 — the GM will tell you when it lands.", + "ends": "The next time you bank a Bane" }, { "id": "encumbered", diff --git a/packs/armour/LOG b/packs/armour/LOG index ea0335e..7360c16 100644 --- a/packs/armour/LOG +++ b/packs/armour/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.342167 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.342695 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.342842 1724f3000 Level-0 table #5: 2618 bytes OK -2026/07/22-22:53:09.342915 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.342966 1724f3000 Manual compaction at level-0 from '!items!GOAakTqMDZSCDOdE' @ 72057594037927935 : 1 .. '!items!vHyUhdri4IKpq9CW' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.379791 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.380374 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.380531 17448f000 Level-0 table #5: 2618 bytes OK +2026/07/28-08:51:35.380609 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.380657 17448f000 Manual compaction at level-0 from '!items!GOAakTqMDZSCDOdE' @ 72057594037927935 : 1 .. '!items!vHyUhdri4IKpq9CW' @ 0 : 0; will stop at (end) diff --git a/packs/bestiary/LOG b/packs/bestiary/LOG index 11d29c3..38e2659 100644 --- a/packs/bestiary/LOG +++ b/packs/bestiary/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.402765 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.403157 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.403344 1724f3000 Level-0 table #5: 5226 bytes OK -2026/07/22-22:53:09.403402 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.403444 1724f3000 Manual compaction at level-0 from '!actors!351sA95uALULY3Kv' @ 72057594037927935 : 1 .. '!actors!rPfEYHmSoprnQdrY' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.440581 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.440964 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.441167 17448f000 Level-0 table #5: 5226 bytes OK +2026/07/28-08:51:35.441259 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.441324 17448f000 Manual compaction at level-0 from '!actors!351sA95uALULY3Kv' @ 72057594037927935 : 1 .. '!actors!rPfEYHmSoprnQdrY' @ 0 : 0; will stop at (end) diff --git a/packs/companions/LOG b/packs/companions/LOG index 9ce1fef..25038e5 100644 --- a/packs/companions/LOG +++ b/packs/companions/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.406577 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.407048 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.407182 1724f3000 Level-0 table #5: 4974 bytes OK -2026/07/22-22:53:09.407246 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.407280 1724f3000 Manual compaction at level-0 from '!actors!1FGl7v1mODKvN9fy' @ 72057594037927935 : 1 .. '!actors!yEfKBqSa4GmIgOpx' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.445181 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.445636 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.445866 17448f000 Level-0 table #5: 4974 bytes OK +2026/07/28-08:51:35.445971 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.446024 17448f000 Manual compaction at level-0 from '!actors!1FGl7v1mODKvN9fy' @ 72057594037927935 : 1 .. '!actors!yEfKBqSa4GmIgOpx' @ 0 : 0; will stop at (end) diff --git a/packs/edges/LOG b/packs/edges/LOG index 06f266c..ca226f9 100644 --- a/packs/edges/LOG +++ b/packs/edges/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.395152 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.395469 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.395594 1724f3000 Level-0 table #5: 5417 bytes OK -2026/07/22-22:53:09.395643 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.395676 1724f3000 Manual compaction at level-0 from '!items!21hRQJtWGk5fDQlo' @ 72057594037927935 : 1 .. '!items!zMphj752klzDFU3Q' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.430932 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.431308 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.431493 17448f000 Level-0 table #5: 5417 bytes OK +2026/07/28-08:51:35.431570 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.431614 17448f000 Manual compaction at level-0 from '!items!21hRQJtWGk5fDQlo' @ 72057594037927935 : 1 .. '!items!zMphj752klzDFU3Q' @ 0 : 0; will stop at (end) diff --git a/packs/gear/LOG b/packs/gear/LOG index 0b958bc..a3d221a 100644 --- a/packs/gear/LOG +++ b/packs/gear/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.357214 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.357850 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.358066 1724f3000 Level-0 table #5: 9529 bytes OK -2026/07/22-22:53:09.358124 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.358162 1724f3000 Manual compaction at level-0 from '!items!27LcrYsIEEF3KYHA' @ 72057594037927935 : 1 .. '!items!yo48EzCVEwNdpkXF' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.389889 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.390383 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.390663 17448f000 Level-0 table #5: 9529 bytes OK +2026/07/28-08:51:35.390746 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.390791 17448f000 Manual compaction at level-0 from '!items!27LcrYsIEEF3KYHA' @ 72057594037927935 : 1 .. '!items!yo48EzCVEwNdpkXF' @ 0 : 0; will stop at (end) diff --git a/packs/gm/000005.ldb b/packs/gm/000005.ldb index d1b314a..ef62e7d 100644 Binary files a/packs/gm/000005.ldb and b/packs/gm/000005.ldb differ diff --git a/packs/gm/LOG b/packs/gm/LOG index d4f62b9..a8aa195 100644 --- a/packs/gm/LOG +++ b/packs/gm/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.428790 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.429078 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.429289 1724f3000 Level-0 table #5: 10149 bytes OK -2026/07/22-22:53:09.429337 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.429371 1724f3000 Manual compaction at level-0 from '!journal!JJEgDJvtWBnFbyZA' @ 72057594037927935 : 1 .. '!journal.pages!kBclT2QiwEQmOsX1.HSVD8c7BenQjb45v' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.469895 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.470270 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.470468 17448f000 Level-0 table #5: 10157 bytes OK +2026/07/28-08:51:35.470538 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.470582 17448f000 Manual compaction at level-0 from '!journal!JJEgDJvtWBnFbyZA' @ 72057594037927935 : 1 .. '!journal.pages!kBclT2QiwEQmOsX1.HSVD8c7BenQjb45v' @ 0 : 0; will stop at (end) diff --git a/packs/gm/MANIFEST-000002 b/packs/gm/MANIFEST-000002 index 7ad9108..dd8024a 100644 Binary files a/packs/gm/MANIFEST-000002 and b/packs/gm/MANIFEST-000002 differ diff --git a/packs/pregens/LOG b/packs/pregens/LOG index 5c2f1be..58b17d5 100644 --- a/packs/pregens/LOG +++ b/packs/pregens/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.418973 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.419962 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.420751 1724f3000 Level-0 table #5: 120609 bytes OK -2026/07/22-22:53:09.420822 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.420867 1724f3000 Manual compaction at level-0 from '!actors!3Kjqu9ibyf8TjSrk' @ 72057594037927935 : 1 .. '!actors.items!zR0OZzFsVQLTSlBM.uc6AKpWyZeREAX2M' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.459780 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.461023 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.461965 17448f000 Level-0 table #5: 120609 bytes OK +2026/07/28-08:51:35.462052 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.462103 17448f000 Manual compaction at level-0 from '!actors!3Kjqu9ibyf8TjSrk' @ 72057594037927935 : 1 .. '!actors.items!zR0OZzFsVQLTSlBM.uc6AKpWyZeREAX2M' @ 0 : 0; will stop at (end) diff --git a/packs/rules/000005.ldb b/packs/rules/000005.ldb index 769576b..01951f8 100644 Binary files a/packs/rules/000005.ldb and b/packs/rules/000005.ldb differ diff --git a/packs/rules/LOG b/packs/rules/LOG index 1fdc4b9..0691d5d 100644 --- a/packs/rules/LOG +++ b/packs/rules/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.424854 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.425505 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.426080 1724f3000 Level-0 table #5: 77503 bytes OK -2026/07/22-22:53:09.426135 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.426181 1724f3000 Manual compaction at level-0 from '!journal!CNrYR2rXwHMBm0lH' @ 72057594037927935 : 1 .. '!journal.pages!CNrYR2rXwHMBm0lH.yPi5N6WdEe1jn19i' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.465802 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.466501 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.467349 17448f000 Level-0 table #5: 76482 bytes OK +2026/07/28-08:51:35.467439 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.467519 17448f000 Manual compaction at level-0 from '!journal!CNrYR2rXwHMBm0lH' @ 72057594037927935 : 1 .. '!journal.pages!CNrYR2rXwHMBm0lH.yPi5N6WdEe1jn19i' @ 0 : 0; will stop at (end) diff --git a/packs/rules/MANIFEST-000002 b/packs/rules/MANIFEST-000002 index ce18d77..75dbe11 100644 Binary files a/packs/rules/MANIFEST-000002 and b/packs/rules/MANIFEST-000002 differ diff --git a/packs/spells/LOG b/packs/spells/LOG index 59616b6..61f9d33 100644 --- a/packs/spells/LOG +++ b/packs/spells/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.388862 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.389229 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.389426 1724f3000 Level-0 table #5: 11569 bytes OK -2026/07/22-22:53:09.389478 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.389511 1724f3000 Manual compaction at level-0 from '!items!13KOIsQOsgYqdVEX' @ 72057594037927935 : 1 .. '!items!zywiqamhRlcCgLJY' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.425396 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.425797 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.426025 17448f000 Level-0 table #5: 11569 bytes OK +2026/07/28-08:51:35.426103 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.426147 17448f000 Manual compaction at level-0 from '!items!13KOIsQOsgYqdVEX' @ 72057594037927935 : 1 .. '!items!zywiqamhRlcCgLJY' @ 0 : 0; will stop at (end) diff --git a/packs/tables/000005.ldb b/packs/tables/000005.ldb index c4a8b0b..c50977a 100644 Binary files a/packs/tables/000005.ldb and b/packs/tables/000005.ldb differ diff --git a/packs/tables/LOG b/packs/tables/LOG index 44f390b..b12f981 100644 --- a/packs/tables/LOG +++ b/packs/tables/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.432699 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.433167 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.433520 1724f3000 Level-0 table #5: 15588 bytes OK -2026/07/22-22:53:09.433594 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.433642 1724f3000 Manual compaction at level-0 from '!tables!JEOcxCxjK6S6pVEZ' @ 72057594037927935 : 1 .. '!tables.results!rurx5ZPcPObdbNDD.bWBU56ZArkeat9DO' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.473894 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.474273 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.474514 17448f000 Level-0 table #5: 11912 bytes OK +2026/07/28-08:51:35.474600 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.474660 17448f000 Manual compaction at level-0 from '!tables!Ci4uLuCx15pTKdhP' @ 72057594037927935 : 1 .. '!tables.results!rurx5ZPcPObdbNDD.bWBU56ZArkeat9DO' @ 0 : 0; will stop at (end) diff --git a/packs/tables/MANIFEST-000002 b/packs/tables/MANIFEST-000002 index d931d38..364672e 100644 Binary files a/packs/tables/MANIFEST-000002 and b/packs/tables/MANIFEST-000002 differ diff --git a/packs/treasure/LOG b/packs/treasure/LOG index efdaf47..15df3c4 100644 --- a/packs/treasure/LOG +++ b/packs/treasure/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.382119 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.382653 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.383100 1724f3000 Level-0 table #5: 27619 bytes OK -2026/07/22-22:53:09.383168 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.383221 1724f3000 Manual compaction at level-0 from '!items!0NDwQbhQQNeCCEco' @ 72057594037927935 : 1 .. '!items!yxds1AZFNXBpERdk' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.417879 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.418464 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.418843 17448f000 Level-0 table #5: 27619 bytes OK +2026/07/28-08:51:35.418931 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.418979 17448f000 Manual compaction at level-0 from '!items!0NDwQbhQQNeCCEco' @ 72057594037927935 : 1 .. '!items!yxds1AZFNXBpERdk' @ 0 : 0; will stop at (end) diff --git a/packs/vices/LOG b/packs/vices/LOG index 5a91a77..4713bc2 100644 --- a/packs/vices/LOG +++ b/packs/vices/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.397830 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.398050 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.398158 1724f3000 Level-0 table #5: 1461 bytes OK -2026/07/22-22:53:09.398227 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.398280 1724f3000 Manual compaction at level-0 from '!items!7kO3VzfFPt8QRVXT' @ 72057594037927935 : 1 .. '!items!s50CZgFUaS5puSOp' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.433688 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.433973 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.434099 17448f000 Level-0 table #5: 1461 bytes OK +2026/07/28-08:51:35.434174 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.434224 17448f000 Manual compaction at level-0 from '!items!7kO3VzfFPt8QRVXT' @ 72057594037927935 : 1 .. '!items!s50CZgFUaS5puSOp' @ 0 : 0; will stop at (end) diff --git a/packs/weapons/LOG b/packs/weapons/LOG index 47694df..07e372a 100644 --- a/packs/weapons/LOG +++ b/packs/weapons/LOG @@ -1,5 +1,5 @@ -2026/07/22-22:53:09.334806 171c5b000 Delete type=3 #1 -2026/07/22-22:53:09.336531 1724f3000 Level-0 table #5: started -2026/07/22-22:53:09.336701 1724f3000 Level-0 table #5: 7415 bytes OK -2026/07/22-22:53:09.336755 1724f3000 Delete type=0 #3 -2026/07/22-22:53:09.336799 1724f3000 Manual compaction at level-0 from '!items!0d6FZCtl78jEPkof' @ 72057594037927935 : 1 .. '!items!v3IghflF7cI2FzbU' @ 0 : 0; will stop at (end) +2026/07/28-08:51:35.372738 173bf7000 Delete type=3 #1 +2026/07/28-08:51:35.375852 17448f000 Level-0 table #5: started +2026/07/28-08:51:35.376112 17448f000 Level-0 table #5: 7415 bytes OK +2026/07/28-08:51:35.376192 17448f000 Delete type=0 #3 +2026/07/28-08:51:35.376246 17448f000 Manual compaction at level-0 from '!items!0d6FZCtl78jEPkof' @ 72057594037927935 : 1 .. '!items!v3IghflF7cI2FzbU' @ 0 : 0; will stop at (end) diff --git a/styles/vanity.css b/styles/vanity.css index 51b5e13..106b8f2 100644 --- a/styles/vanity.css +++ b/styles/vanity.css @@ -592,6 +592,30 @@ .vanity-buttons button:hover { background: var(--v-teal-d); } .vanity-buttons .vanity-fizzle { background: var(--v-plum); } +/* The Bane decision: burn a Success, or take the Bane */ +.vanity-bane-choice { + margin-top: 7px; padding: 8px 10px; border-radius: 5px; + background: rgba(138, 31, 31, 0.08); border: 1px solid rgba(138, 31, 31, 0.35); +} +.vanity-bane-choice .warn { + display: block; color: #8a1f1f; font-size: 12px; line-height: 1.35; + margin-bottom: 7px; text-transform: none; letter-spacing: normal; +} +.vanity-bane-choice .vanity-buttons { margin-top: 0; } +.vanity-bane-choice .vanity-buttons button { + display: inline-flex; align-items: center; justify-content: center; gap: 5px; + line-height: 1.2; letter-spacing: 0.02em; padding: 8px 6px; text-align: center; +} +.vanity-bane-choice .vanity-burn { background: var(--v-gold); color: #2a1c06; } +.vanity-bane-choice .vanity-burn:hover { background: #caa23f; } +.vanity-bane-choice .vanity-takebane { background: #8a1f1f; color: var(--v-paper); } +.vanity-bane-choice .vanity-takebane:hover { background: #6f1717; } + +/* Momentum — a great roll earns a die on the next Draw */ +.vanity-momentum { border-color: var(--v-gold); } +.vanity-momentum .vanity-roll-label, .vanity-momentum p b { color: var(--v-gold); } +.vanity-sheet .roll-draw .mo { color: var(--v-gold); font-weight: 700; margin-left: 3px; } + /* ---------------- Roll dialog ---------------- */ .vanity-roll-dialog .pool-line { font-size: 14px; } diff --git a/system.json b/system.json index a272b7e..6c33918 100644 --- a/system.json +++ b/system.json @@ -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), ten animal companions with the d10 Bond table and the one-shot adventure The Vain Crown.", - "version": "0.10.2", + "description": "A generic d6 RPG where ego is a resource and the universe keeps score. Pool = Attribute + Knack + situation; 5–6 = a Success; two or more 1s bank a Bane of Karma unless you burn a Success to cancel one (Draft 14). 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.10.3", "compatibility": { "minimum": 13, "verified": "14.364" diff --git a/template.json b/template.json index 8e89fed..3a1ce0f 100644 --- a/template.json +++ b/template.json @@ -90,7 +90,8 @@ "notes": "" }, "notes": "", - "biography": "" + "biography": "", + "momentum": 0 }, "npc": { "category": "mook", diff --git a/templates/character-sheet.hbs b/templates/character-sheet.hbs index 17f7177..85ba969 100644 --- a/templates/character-sheet.hbs +++ b/templates/character-sheet.hbs @@ -44,7 +44,7 @@ {{/each}} -
Pool = Attribute + Knack + situation. 5–6 = a Success. Fail with two 1s = a Stumble (1 Bane).
+
Pool = Attribute + Knack + situation. 5–6 = a Success. Two or more 1s bank a Bane — burn a Success to cancel one.
@@ -80,7 +80,7 @@ {{#each karmaPips}}{{/each}} -
Gain: Stumble · Push · Refuse to Fall · your Vice. 6 → THE RECKONING (2d6, §21b). Clear by humility & rest.
+
Gain: two fails · Push · Refuse to Fall · your Vice. 6 → THE RECKONING (2d6, §21b). Clear by humility & rest.
Glory @@ -159,7 +159,7 @@ {{!-- ============ TAB: COMBAT & GEAR ============ --}}
- + diff --git a/vanity.mjs b/vanity.mjs index 14646fa..8518ba9 100644 --- a/vanity.mjs +++ b/vanity.mjs @@ -287,7 +287,7 @@ class VanityActor extends Actor {
${karma >= 6 ? `` - : ``} + : ``}
` }); @@ -296,6 +296,16 @@ class VanityActor extends Actor { class VanityItem extends Item {} +/** The Draw with Momentum: a hero who earned Momentum (a 3+ Success roll) adds dice + * to their next initiative and keeps the highest. Consumed as it is rolled. */ +class VanityCombatant extends foundry.documents.Combatant { + _getInitiativeFormula() { + const m = Math.max(0, Math.floor(this.actor?.system?.momentum ?? 0)); + if (m > 0 && this.actor?.isOwner) this.actor.update({ "system.momentum": 0 }).catch(() => {}); + return m > 0 ? `${1 + m}d6kh1` : (CONFIG.Combat.initiative?.formula || "1d6"); + } +} + /* -------------------------------------------- */ /* The dice engine */ /* -------------------------------------------- */ @@ -315,10 +325,21 @@ function renderDice(results) { /** * Build the chat-card HTML for a pool roll. */ -function renderRollCard({ label, sublabel, results, threshold, pushed, context, extra, spend, banked = 0, twistUsed = false, spell = null, target = null }) { - const successes = results.filter(r => r >= 5).length; - const ones = results.filter(r => r === 1).length; - const stumble = successes === 0 && ones >= 2; +/** The tab, resolved (Draft 14.1). Two+ fails threaten a Bane regardless of Successes; + * a player may burn 1 Success to cancel one fail (drop below two dodges the Bane). A + * single remaining fail is the GM's free Twist. `canceled` = fails cancelled so far. */ +function baneState(results, canceled = 0) { + const rawSucc = results.filter(r => r >= 5).length; + const rawOnes = results.filter(r => r === 1).length; + const succ = Math.max(0, rawSucc - canceled); + const ones = Math.max(0, rawOnes - canceled); + return { rawSucc, rawOnes, canceled, succ, ones, + threat: ones >= 2, canBurn: ones >= 2 && succ >= 1, singleFail: ones === 1 }; +} + +function renderRollCard({ label, sublabel, results, threshold, pushed, context, extra, spend, canceled = 0, baneResolved = null, isChar = false, twistUsed = false, spell = null, target = null }) { + const st = baneState(results, canceled); + const successes = st.succ; const met = threshold ? successes >= threshold : successes >= 1; let outcome; @@ -344,6 +365,26 @@ function renderRollCard({ label, sublabel, results, threshold, pushed, context, ? renderSpendPanel({ successes, spend, sublabel, menu: menuFor(context), context, threshold }) : ""; + // ── the tab ── 2+ fails threaten a Bane (burn 1 Success to cancel one); a single fail is a free twist. + const pending = isChar && st.threat && baneResolved !== "banked" && baneResolved !== "cleared"; + let baneUI = ""; + if (isChar && baneResolved === "banked") { + baneUI = `
· ${st.canceled ? `burned ${st.canceled} Success${st.canceled > 1 ? "es" : ""}, then ` : ""}the tab grows — +1 Bane banked
`; + } else if (pending) { + const burn = st.canBurn + ? `` + : ""; + baneUI = `
+ ${st.ones} fails — the tab threatens a Bane.${st.canBurn ? " Burn a Success to cancel one, or take it." : " No Success to spare — it banks."} +
${burn}
+
`; + } else if (isChar && baneResolved === "cleared") { + baneUI = `
· burned ${st.canceled} Success${st.canceled > 1 ? "es" : ""} — the tab stays clean
`; + } + const freeTwist = isChar && st.singleFail && !twistUsed && !pending && baneResolved !== "banked" + ? `
` + : (twistUsed ? `
The knife has been twisted.
` : ""); + const spellBanner = spell ? `
@@ -367,16 +408,14 @@ function renderRollCard({ label, sublabel, results, threshold, pushed, context,
${renderDice(results)}
${outcome} - ${stumble - ? `· a STUMBLE${banked ? " — 1 Bane banked" : ""}` - : ones ? `· ${ones === 1 ? "a lone 1" : `${ones} ones`} — the GM may twist the knife now` : ""} + ${st.ones && !pending && baneResolved !== "banked" && baneResolved !== "cleared" + ? `· ${st.ones === 1 ? "a lone 1 — a single fail" : `${st.ones} ones`}` : ""} ${pushed ? `· Pushed` : ""}
- ${!stumble && ones && !twistUsed - ? `
` - : twistUsed ? `
The knife has been twisted.
` : ""} + ${baneUI} + ${freeTwist} ${spellScaling} - ${targetBlock(target, results, spend, context)} + ${targetBlock(target, results, spend, context, canceled)} ${extra ? `
${extra}
` : ""} ${specials} ${buttons ? `
${buttons}
` : ""} @@ -389,9 +428,9 @@ function renderRollCard({ label, sublabel, results, threshold, pushed, context, * charge of the fiction — Subdue, mercy and "he was already down" all live in * the beat between the roll and the click. */ -function targetBlock(target, results, spend, context = "attack") { +function targetBlock(target, results, spend, context = "attack", canceled = 0) { if (!target) return ""; - const successes = results.filter(r => r >= 5).length; + const successes = Math.max(0, results.filter(r => r >= 5).length - canceled); const net = Math.max(0, successes - (spend?.defence ?? target.successes ?? 0)); // In an exchange the first net Success IS the hit — 1 Grit before anything is // bought. Only a spell starts from nothing, because its Threshold bought the @@ -468,11 +507,16 @@ async function rollPool(actor, pool, label, options = {}) { lockVanityDice(roll); const results = roll.dice[0].results.map(r => r.result); - // The Stumble (Draft 11): bank 1 Bane only on a failed roll with two or more 1s. - const ones = results.filter(r => r === 1).length; - const successes = results.filter(r => r >= 5).length; - const stumble = successes === 0 && ones >= 2; - const banked = stumble && actor?.type === "character" ? 1 : 0; + // Two or more fails threaten a Bane regardless of Successes. If the roller can burn + // (1 Success cancels one fail) they decide on the card; if not, it banks automatically. + // A single fail is the GM's free twist, not a Bane. + const isChar = actor?.type === "character"; + const st = baneState(results, 0); + let baneResolved = null, autoBank = 0; + if (isChar && st.threat) { + if (st.canBurn) baneResolved = null; // pending — the player chooses + else { baneResolved = "banked"; autoBank = 1; } // no Success to burn — it banks + } const flags = { vanity: { @@ -486,7 +530,9 @@ async function rollPool(actor, pool, label, options = {}) { actorUuid: actor?.uuid ?? "", pushed: false, extra: "", - banked, + canceled: 0, + baneResolved, + isChar, spell: options.spell ?? null, target: options.target ?? null, spend: options.spend ?? { defence: 0, damage: 0, maneuvers: [] } @@ -499,12 +545,26 @@ async function rollPool(actor, pool, label, options = {}) { sound: CONFIG.sounds.dice, content: renderRollCard({ label, sublabel: options.sublabel, results, threshold: options.threshold, pushed: false, - context: options.context, banked, spell: options.spell ?? null, + context: options.context, canceled: 0, baneResolved, isChar, spell: options.spell ?? null, target: options.target ?? null, spend: flags.vanity.spend }), flags }); - if (banked) await actor.addBanes(1, `a Stumble on “${label}”`); + if (autoBank) await actor.addBanes(1, `two fails on “${label}”`); + + // Momentum: a roll of 3+ Successes (two beyond the one needed) earns a die on the + // next Draw. Caps at +2; a fresh grant only heralds when it actually rises. + if (isChar && st.succ >= 3 && options.context !== "draw") { + const old = actor.system.momentum ?? 0; + const m = Math.min(2, old + 1); + if (m > old) { + await actor.update({ "system.momentum": m }); + await ChatMessage.create({ + speaker: ChatMessage.getSpeaker({ actor }), + content: `

✦ Momentum — ${st.succ} Successes. ${actor.name} carries +${m} die on their next Draw (keep highest).

` + }); + } + } } /* -------------------------------------------- */ @@ -680,16 +740,19 @@ async function handlePush(message) { if (game.dice3d) await game.dice3d.showForRoll(roll, game.user, true); } - // Push costs 1 Bane; a pushed roll that STILL fails with two or more 1s Stumbles for another. - const successes = newResults.filter(r => r >= 5).length; - const ones = newResults.filter(r => r === 1).length; - const pushedStumble = successes === 0 && ones >= 2; + // The Push always bills 1 Bane (chosen hubris). The pushed result's 2+ fails then + // threaten a further Bane, which the player may burn a Success to dodge on the card. const isCharacter = actor?.type === "character"; - const paid = 1 + (pushedStumble ? 1 : 0); - const banked = pushedStumble && isCharacter ? 1 : 0; + const st = baneState(newResults, 0); + let baneResolved = null, autoBank = 0; + if (isCharacter && st.threat) { + if (st.canBurn) baneResolved = null; + else { baneResolved = "banked"; autoBank = 1; } + } + const successes = st.succ; - let extra = pushedStumble - ? `${game.i18n.localize("VANITY.Pushed")} — and pushed straight into a Stumble (+${paid} Banes).` + let extra = autoBank + ? `${game.i18n.localize("VANITY.Pushed")} — and straight into two fails (+2 Banes).` : `${game.i18n.localize("VANITY.Pushed")} (+1 Bane).`; // A failed Push on a spell is a CATASTROPHE. @@ -709,16 +772,18 @@ async function handlePush(message) { await message.update({ content: renderRollCard({ label: f.label, sublabel: f.sublabel, results: newResults, - threshold: f.threshold, pushed: true, context: f.context, extra, spend, banked, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null + threshold: f.threshold, pushed: true, context: f.context, extra, spend, + canceled: 0, baneResolved, isChar: isCharacter, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null }), "flags.vanity.results": newResults, "flags.vanity.pushed": true, "flags.vanity.extra": extra, - "flags.vanity.banked": banked, + "flags.vanity.canceled": 0, + "flags.vanity.baneResolved": baneResolved, "flags.vanity.spend": spend }); if (isCharacter) { - await actor.addBanes(paid, pushedStumble ? `Pushed “${f.label}” straight into a Stumble` : `Pushed “${f.label}”`); + await actor.addBanes(1 + autoBank, autoBank ? `Pushed “${f.label}” into two fails` : `Pushed “${f.label}”`); } } @@ -726,7 +791,7 @@ async function handlePush(message) { async function handleSpend(message, dataset) { const f = message.flags?.vanity; if (!f) return; - const successes = f.results.filter(r => r >= 5).length; + const successes = baneState(f.results, f.canceled ?? 0).succ; const spend = { defence: 0, damage: 0, maneuvers: [], ...(f.spend ?? {}) }; if (dataset.def !== undefined) { @@ -751,12 +816,57 @@ async function handleSpend(message, dataset) { await message.update({ content: renderRollCard({ label: f.label, sublabel: f.sublabel, results: f.results, - threshold: f.threshold, pushed: f.pushed, context: f.context, extra: f.extra, spend, banked: f.banked ?? 0, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null + threshold: f.threshold, pushed: f.pushed, context: f.context, extra: f.extra, spend, canceled: f.canceled ?? 0, baneResolved: f.baneResolved ?? null, isChar: f.isChar, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null }), "flags.vanity.spend": spend }); } +/** Burn 1 Success to cancel one fail. Drop below two fails and the Bane is dodged. */ +async function handleBurn(message) { + const f = message.flags?.vanity; + if (!f || f.baneResolved === "banked" || f.baneResolved === "cleared") return; + if (!baneState(f.results, f.canceled ?? 0).canBurn) return; + const canceled = (f.canceled ?? 0) + 1; + const st = baneState(f.results, canceled); + const baneResolved = st.threat ? (f.baneResolved ?? null) : "cleared"; + + // Fewer Successes may make a prior attack-spend illegal — trim to the new budget. + const spend = { defence: f.spend?.defence ?? 0, damage: f.spend?.damage ?? 0, maneuvers: [...(f.spend?.maneuvers ?? [])] }; + const budget = f.context === "spell" + ? Math.max(0, st.succ - (f.threshold || 1)) + : Math.max(0, st.succ - spend.defence - 1); + const cost = spend.damage + spend.maneuvers.reduce((n, k) => n + (menuFor(f.context).find(m => m.key === k)?.cost ?? 0), 0); + if (cost > budget) { spend.damage = 0; spend.maneuvers = []; } + + await message.update({ + content: renderRollCard({ + label: f.label, sublabel: f.sublabel, results: f.results, threshold: f.threshold, + pushed: f.pushed, context: f.context, extra: f.extra, spend, + canceled, baneResolved, isChar: f.isChar, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null + }), + "flags.vanity.canceled": canceled, + "flags.vanity.baneResolved": baneResolved, + "flags.vanity.spend": spend + }); +} + +/** Accept the Bane rather than burn Successes: the tab grows by one. */ +async function handleTakeBane(message) { + const f = message.flags?.vanity; + if (!f || f.baneResolved === "banked" || f.baneResolved === "cleared") return; + await message.update({ + content: renderRollCard({ + label: f.label, sublabel: f.sublabel, results: f.results, threshold: f.threshold, + pushed: f.pushed, context: f.context, extra: f.extra, spend: f.spend, + canceled: f.canceled ?? 0, baneResolved: "banked", isChar: f.isChar, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null + }), + "flags.vanity.baneResolved": "banked" + }); + const actor = f.actorUuid ? await fromUuid(f.actorUuid) : null; + if (actor?.type === "character") await actor.addBanes(1, `two fails on “${f.label}”`); +} + async function handleFizzle(message) { const f = message.flags?.vanity; if (!f || f.pushed) return; @@ -768,7 +878,7 @@ async function handleFizzle(message) { await message.update({ content: renderRollCard({ label: f.label, sublabel: f.sublabel, results: f.results, - threshold: f.threshold, pushed: true, context: f.context, extra, banked: f.banked ?? 0, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null + threshold: f.threshold, pushed: true, context: f.context, extra, canceled: f.canceled ?? 0, baneResolved: f.baneResolved ?? null, isChar: f.isChar, twistUsed: f.twistUsed ?? false, spell: f.spell ?? null, target: f.target ?? null }), "flags.vanity.pushed": true, "flags.vanity.extra": extra @@ -953,9 +1063,18 @@ class VanityActorSheet extends foundry.appv1.sheets.ActorSheet { html.find(".roll-draw").click(async () => { if (actor.inCombat) { const c = game.combat?.combatants.find(c => c.actorId === actor.id); - if (c) return game.combat.rollInitiative([c.id]); + if (c) return game.combat.rollInitiative([c.id]); // VanityCombatant folds in Momentum } - await rollPool(actor, 1, "The Draw (initiative)", { sublabel: "act highest to lowest — reroll each round" }); + // Out of combat: 1d6 + any Momentum dice, keep the highest; consume the Momentum. + const m = Math.max(0, Math.floor(actor.system.momentum ?? 0)); + const roll = new Roll(`${1 + m}d6kh1`); + await roll.evaluate(); + lockVanityDice(roll); + if (m) await actor.update({ "system.momentum": 0 }); + await roll.toMessage({ + speaker: ChatMessage.getSpeaker({ actor }), + flavor: `The Draw (initiative)${m ? ` — +${m} Momentum` : ""} · act highest to lowest` + }); }); // Weapon attack @@ -1321,7 +1440,7 @@ Hooks.on("diceSoNiceRollStart", (messageId, context) => { /* Twist the Knife — the GM spends the tab */ /* -------------------------------------------- */ -const TWIST_TABLE_NAME = "Twist the Knife — spending Banes (d66)"; +const TWIST_TABLE_NAME = "Twist the Knife — a single fail (2d6)"; const RECKONING_TABLE_NAME = "THE RECKONING — the tab reads six (2d6)"; const BOND_TABLE_NAME = "The Bond — Animal Companions (d10)"; @@ -1387,7 +1506,7 @@ async function twistTheKnife({ actorUuid = "", free = false, sourceMessage = nul const table = await getVanityTable(TWIST_TABLE_NAME); if (!table) return ui.notifications.error(`"${TWIST_TABLE_NAME}" not found in world or compendium.`); - const roll = new Roll("1d6*10 + 1d6"); + const roll = new Roll("2d6"); await roll.evaluate(); await showBaneDice(roll); // table.draw({roll}) re-rolls internally — map our roll to its result so the red dice @@ -1415,7 +1534,7 @@ async function twistTheKnife({ actorUuid = "", free = false, sourceMessage = nul content: renderRollCard({ label: f.label, sublabel: f.sublabel, results: f.results, threshold: f.threshold, pushed: f.pushed, context: f.context, extra: f.extra, spend: f.spend, - banked: f.banked ?? 0, twistUsed: true, spell: f.spell ?? null, target: f.target ?? null + canceled: f.canceled ?? 0, baneResolved: f.baneResolved ?? null, isChar: f.isChar, twistUsed: true, spell: f.spell ?? null, target: f.target ?? null }) }); } @@ -2039,6 +2158,7 @@ Hooks.once("init", () => { CONFIG.specialStatusEffects.DEFEATED = "taken-out"; CONFIG.Actor.documentClass = VanityActor; + CONFIG.Combatant.documentClass = VanityCombatant; CONFIG.Item.documentClass = VanityItem; CONFIG.Combat.initiative = { formula: "1d6", decimals: 0 }; @@ -2106,6 +2226,52 @@ Hooks.on("combatRound", async (combat, updateData, updateOptions) => { await combat.rollAll(); }); +/* Make an Entrance — promote a combatant to the top of The Draw without re-rolling + * for a 6. Right-click a combatant in the tracker. */ +function combatantIdFromEntry(li) { + const el = li?.[0] ?? li; + return el?.dataset?.combatantId + ?? el?.closest?.("[data-combatant-id]")?.dataset?.combatantId + ?? (li?.data ? li.data("combatant-id") : null); +} +async function promoteToTop(combatant) { + const combat = combatant?.combat ?? game.combat; + if (!combat || !combatant) return; + const top = Math.max(0, ...combat.combatants.map(c => (Number.isFinite(c.initiative) ? c.initiative : 0))); + await combatant.update({ initiative: top + 1 }); +} +Hooks.on("getCombatTrackerEntryContext", (html, options) => { + options.push({ + name: "VANITY: Go first — make an entrance (1 Vanity)", + icon: '', + condition: li => { + const c = game.combat?.combatants.get(combatantIdFromEntry(li)); + return !!c && c.actor?.type === "character" && (game.user.isGM || c.actor?.isOwner); + }, + callback: async li => { + const c = game.combat?.combatants.get(combatantIdFromEntry(li)); + if (!c?.actor) return; + const van = c.actor.system.vanity ?? 0; + if (van < 1) return ui.notifications.warn(`${c.actor.name} has no Vanity to spend on an entrance.`); + await c.actor.update({ "system.vanity": van - 1 }); + await promoteToTop(c); + await ChatMessage.create({ + speaker: { alias: "The Draw" }, + content: `

${c.name} spends 1 Vanity and makes an entrance — first in the round.

` + }); + } + }); + options.push({ + name: "VANITY: Move to top of the order", + icon: '', + condition: () => game.user.isGM, + callback: async li => { + const c = game.combat?.combatants.get(combatantIdFromEntry(li)); + if (c) await promoteToTop(c); + } + }); +}); + /* Chat card buttons. */ Hooks.on("renderChatMessageHTML", (message, html) => { html.querySelectorAll(".vanity-push").forEach(btn => { @@ -2136,6 +2302,22 @@ Hooks.on("renderChatMessageHTML", (message, html) => { applyDamageToTarget(message, Number(btn.dataset.damage) || 0); }); }); + html.querySelectorAll(".vanity-burn").forEach(btn => { + btn.addEventListener("click", () => { + if (!game.user.isGM && message.author?.id !== game.user.id) { + return ui.notifications.warn("Only the roller (or the GM) may burn Successes."); + } + handleBurn(message); + }); + }); + html.querySelectorAll(".vanity-takebane").forEach(btn => { + btn.addEventListener("click", () => { + if (!game.user.isGM && message.author?.id !== game.user.id) { + return ui.notifications.warn("Only the roller (or the GM) may take the Bane."); + } + handleTakeBane(message); + }); + }); html.querySelectorAll(".vanity-twist").forEach(btn => { btn.addEventListener("click", () => { if (!game.user.isGM) return ui.notifications.warn("Only the GM may twist the knife.");