Skip to content

v6.10.50

Choose a tag to compare

@github-actions github-actions released this 27 Jul 01:48
· 706 commits to main since this release

[6.10.50] — 2026-07-26 — Automated camping, applied carousing outcomes, and Item Piles compatibility

Added

  • Camping is now a complete Party rest procedure rather than an informational task list. The GM can choose participating characters, select each character's task and ability from dropdowns, or begin from the existing drag-and-drop travel assignments. A campfire can consume three pooled inactive torches or be earned by a successful Firewood check; without one, every task marked by the camping rules rolls with disadvantage. The procedure consumes pooled rations, optionally advances world time by eight hours, restores qualifying characters to maximum HP, refreshes lost spells, class abilities, limited uses, and wand spells, and removes unconscious after a successful rest. Tangible task outcomes are applied automatically: Cook raises ration-eating campers up to 2 HP above maximum, Craft creates ammunition or a torch or repairs gear, Entertain grants luck, Hunt adds 1d4 rations before meals are allocated, Keep Watch records the protected half of the rest, and Predict grants a weather re-roll. A final chat summary records supplies, recovery, checks, and benefits.

  • Party weather can use a GM-selected RollTable. A new world setting selects any world or compendium RollTable for the Party sheet's Weather button while preserving the built-in Shadowdark weather roll as the fallback. Successful Predict tasks are offered after tomorrow's weather is revealed and can redraw either source.

  • Travel and camping checks now use the cinematic SDX Roller with activity artwork and per-character abilities. Each configured activity can supply a banner image, and characters in the same group check can roll different allowed abilities without splitting the task into separate cards.

  • Apply carousing outcomes to character sheets, and a Carousing Log. Original-mode outcomes were display-only — the GP cost was deducted, but "Gain 4 XP", "a luck token", and "bilked you for 5% of your total wealth" were rendered as prose and never touched the sheet. (A applyOutcomeEffect() helper for exactly this existed but was dead code, called from nowhere.) Each result card now carries a GM-only Apply button showing a preview of what it will grant; clicking it opens a confirm dialog listing the exact numbers before committing, then writes XP, luck tokens, the percentage wealth loss, and any renown delta. It is idempotent — the session records what was applied, so a second click is a no-op and the card shows an Applied badge instead. Narrative rewards (a priest ally, a debt owed by a noble, being barred from a tavern) have no mechanical target in Shadowdark, so they are deliberately never parsed; instead the full outcome text is appended to the character's Notes, preserving whatever was already there. Expanded mode is excluded, since it already grants its numeric XP column automatically at roll time. Alongside this, a GM-only Carousing Log journal records every session — each character's roll, what happened, the benefit, and exactly what was applied — created create-or-update per session so re-rolling never duplicates a page, and located by flag so renaming it does not orphan the log.

  • Carousing coin maths is now copper-precise, and cards show coins vs. total wealth. Wealth percentages were computed and deducted in whole gp, so change was discarded twice over: getActorTotalGp() floored sp/10 and cp/100 (a purse of 9 sp + 99 cp read as 0 GP), and the percentage was floored again. A 5% loss against 41 gp took 2 gp instead of 2 gp 5 cp. All wealth arithmetic now runs in copper and only converts for display — the deduction spends the smallest coins first and breaks a larger denomination only when the remainder demands it, so a purse is never silently normalised (in Shadowdark 100 coins is a gear slot regardless of denomination, so re-minting a purse would change encumbrance). The old hand-rolled change logic in deductCoins, which could leave leftover silver unspent, now routes through the same copper path. Effect summaries read as real coin ("-5% wealth (2 gp 5 cp)"). Participant cards show 41 / 162 GP — coins carried over total wealth including gear — with the exact breakdown on hover, and the second figure only appears when the character actually has valued gear.

  • The active wealth base is now visible at a glance. A tag beside "Cost per player" reads Wealth base: coins only or Wealth base: coins + gear, so the GM can see which basis a percentage loss will use without opening Settings.

  • New setting: Carousing Wealth Base. Chooses what "N% of your total wealth" is measured against — coins only (gp + sp/10 + cp/100), or coins plus the value of carried gear so stockpiling equipment cannot dodge the penalty. The deduction always comes out of coins either way and cannot drive the purse negative. If a gear-based loss exceeds the coins available, the unpaid balance becomes an accumulating, zero-slot Carousing Debt item on the character sheet, with an exact coin total and a history of the outcomes that created it. Items with no cost recorded count as nothing rather than being guessed at.

Changed

  • Hex Painter map formatting now supports maps up to 200×200 hexes. Both sliders and the runtime clamp were raised from 50 while retaining the same exact-grid sizing formula, which was live-verified beyond the former limit.

Fixed

  • Player camping choices now persist on GM-owned Party actors. Task and ability dropdowns, drag/drop assignment, removal, right-click ability cycling, and Predict consumption now route through the active GM. Every request is validated against Party membership and ownership of the requesting player's character before the Party flags are changed; failures report an error instead of silently reverting.

  • Hidden Expanded Carousing outcomes no longer leak through character Notes. The automatic note writer now honors the same independent benefit/mishap visibility settings as chat. The GM-only log still keeps the complete result, while a player-owned sheet receives only descriptions the GM chose to reveal plus the visible applied XP/Renown summary.

  • Carousing rerolls no longer carry phantom participants into later log pages. Completing either roll mode replaces the stored result collection instead of merging it, and narrative-only Original outcomes now log as Applied even when no numeric summary was produced.

  • Keep Watch and Predict now ship their configured SDX Roller banner art. Both default task image paths resolve to matching black-and-white activity banners instead of silently falling back to the generic image.

  • Different unidentified potions no longer collapse into the first potion placed in an Item Pile. Item Piles similarity matching now includes Shadowdark's hidden identified name, so an unidentified Potion of Healing, Sleep, and Flight remain distinct stacks. Weapons and armor stored in Item Piles are also forced unequipped, and weapon glow/animation effects are suppressed on pile tokens.

  • SDX group rolls now create one recap and cannot strand an awaiting camping procedure. Only the browser that launched a roll creates its recap, eliminating the three-card duplication seen on travel rolls. Replacing or directly closing an overlay now resolves its pending result as canceled, stale overlays cannot clear a newer roll, and the GM close action no longer calls the Foundry close lifecycle twice.

  • Expanded Carousing neither populated the log nor added outcomes to character Notes. Only the Original roll path assigned a log ID, wrote a journal page, and appended its narrative outcome to the sheet after Apply; Expanded mode returned after saving transient results, leaving benefits and mishaps visible only in chat. Expanded rolls now create the same stable per-session log page, with dedicated Outcome, Benefits, and Mishaps columns, and append each participant's player-visible narrative result plus applied XP/renown summary to system.notes. Opening the log backfills the current completed session, and per-session markers make note recovery idempotent so reopening it cannot duplicate entries.

  • Reset Carousing did nothing, and journal-pin flags were never unset. Both passed foundry.data.operators.ForcedDeletion — the class — where Foundry expects an instance, so the update wrote a meaningless value instead of deleting the flag and every reset silently no-opped. All four call sites now construct the operator. Reset clears the session and character drops as intended while leaving the GM's added-actor roster in place.

  • Expanded carousing results showed "No specific outcome recorded" with a blank roll total. Two faults compounded. First, the result card decided which layout to draw with {{#if this.result.xp}} — a truthiness test on a number that is legitimately 0 on a low roll, so any 0 XP outcome fell through to the Original layout, which then looked up fields Expanded results do not have (result.roll rather than result.outcomeRoll), producing "Roll: 4 + 0 = " and the empty-outcome message. The card now branches on the actual carousing mode. Second, and the reason every roll had 0 XP: a linked RollTable whose format does not match the carousing table silently overwrote the GM's configured values. An Expanded table linked to an Original-format outcome table (rows of description | benefit text) parsed to 0 mishaps / 0 benefits / 0 modifier / 0 XP on every row, and that all-zero result was overlaid on top of 25 correctly configured rows on every refresh. Linked data is now only overlaid when the parse yields something usable, per section — so a mismatched link leaves the stored values intact — and the GM gets a warning naming the offending table instead of silently losing their work.

  • Carousing session edits never actually deleted anything, stranding results and resurrecting cleared state. Foundry merges flag objects on write, so removing a key requires an explicit deletion operator — but five carousing functions mutated a local copy of the session and wrote it back, meaning every delete only ever changed the in-memory object. It looked correct until the next reload, when the "removed" data came back. Concretely: unconfirming a player did not persist; clearing a modifier left the old value; clearing or changing a character drop left a stale confirmation and result behind; pruneOfflineCarousingData pruned nothing; and removing a GM-managed participant dropped the actor from the list (arrays do replace wholesale) while orphaning its result — a result still in the session but with no participant to render it, so it could never be reviewed or applied. All five now use Foundry v14's ForcedDeletion operator, avoiding the deprecated -= update syntax. Verified against the document source rather than the in-memory copy, since getFlag returns a live reference that makes a failed delete look like it worked.

  • Carousing results are no longer stranded when a participant leaves the list. A rolled result now keeps its participant alive: the participant list includes any actor with a result even if it is no longer GM-managed, and offline pruning skips players who have one. Explicitly removing a participant still deletes its result outright, so this only rescues outcomes that would otherwise become unreachable.

  • Carousing table imports ignored | column separators, collapsing every entry into one field. RollTables authored outside SDX use a pipe to break a result into its columns — 30 gp | A worthy night of drinking and festivity | +0 — but the importers only understood the labeled Cost 30 gp, Event …, Bonus +0 text the companion table-builder emits. Anything pipe-delimited fell through to the "unrecognised" path, so a Carousing Event table imported with cost 0 and bonus 0 on every tier and the whole line jammed into the description, and a Carousing Outcome table imported with an empty benefit column. Both editors (Original and Expanded) now split on | first, across all four sub-tables: event tiers ([roll |] cost gp | description | bonus), original outcomes ([roll |] description | benefit), expanded outcomes ([roll |] mishaps | benefits | d100 modifier | xp), and the d100 benefit/mishap lists ([roll |] description). The roll column is optional — a RollTable result's range still supplies the roll — and partial column sets degrade safely: the last remaining column is always kept as the description, so a cost-only or bonus-only row never loses its text. The labeled and plain-text formats still parse unchanged, and the paste-from-text dialogs accept pipes too, falling back to their existing whitespace parsing for lines without one.

  • Aura effects threw on every token move, wall edit, and scene change on canvas-less clients. Aura geometry is canvas-derived end to end — token placeables and their centers, grid size, wall collision for line of sight, the visibility API — but the handlers gated on isGM rather than the active GM, so a client running with the core noCanvas setting (an always-on headless relay GM, for instance) ran them all and threw every time. Aura processing now stands down entirely on such a client, logging once at startup rather than registering the hooks at all; any other connected GM still processes auras, so nothing is lost and duplicate processing is avoided. Separately, the hooks and query functions now check that the canvas is actually initialised, which also covers a normal client whose canvas is not ready yet or that has no active scene.


Read the Shadowdark Extras Wiki