diff --git a/i18n/en.yaml b/i18n/en.yaml index 062b0414e..45efc0068 100644 --- a/i18n/en.yaml +++ b/i18n/en.yaml @@ -164,11 +164,9 @@ SHADOWDARK.chat.spell_roll.title: "{name}, DC {spellDC}" SHADOWDARK.chat.use_ability.failure: "{name} failed to used the {ability} ability" SHADOWDARK.chat.use_ability.success: "{name} successfully used the {ability} ability" SHADOWDARK.chat.use_ability.title: "Using Ability" -SHADOWDARK.chat.welcome_message.intro: "To quickly get familiar with the system, check out the provided guided tours by clicking on the following button:" SHADOWDARK.chat.welcome_message.issue_tracker_button: Issue Tracker SHADOWDARK.chat.welcome_message.issues: "If you find any issues or have ideas for new features for the system. Check out our issue tracker:" SHADOWDARK.chat.welcome_message.title: Shadowdark RPG for Foundry -SHADOWDARK.chat.welcome_message.tour_button: Shadowdark RPG Tours SHADOWDARK.chatcard.default: Roll SHADOWDARK.class-ability.ability.check: Ability Check SHADOWDARK.class-ability.ability.label: Ability diff --git a/system/shadowdark.mjs b/system/shadowdark.mjs index 681293b16..200c4ddc9 100644 --- a/system/shadowdark.mjs +++ b/system/shadowdark.mjs @@ -15,7 +15,6 @@ import * as documents from "./src/documents/_module.mjs"; import * as sheets from "./src/sheets/_module.mjs"; import { ModuleArt } from "./src/utils/module-art.mjs"; -import { ToursSD } from "./src/tours.mjs"; import { HooksSD, @@ -128,8 +127,6 @@ Hooks.on("ready", async () => { HooksSD.attach(); listenOnSocket(); - ToursSD.register(); - chat.messages.welcomeMessage(); UtilitySD.showNewReleaseNotes(); diff --git a/system/src/chat/messages.mjs b/system/src/chat/messages.mjs index 0621b3fdf..e4aefb1f9 100644 --- a/system/src/chat/messages.mjs +++ b/system/src/chat/messages.mjs @@ -25,11 +25,6 @@ export async function welcomeMessage() { function _initListeners() { // Add listeners - $(document).on("click", "button.shadowdark-tours", event => { - event.preventDefault(); - shadowdark.log("Importing a Shadowdarkling"); - new ToursManagement().render(true); - }); $(document).on("click", "button.shadowdark-issue-tracker", event => { event.preventDefault(); diff --git a/system/src/tours.mjs b/system/src/tours.mjs deleted file mode 100644 index 01acd89a1..000000000 --- a/system/src/tours.mjs +++ /dev/null @@ -1,23 +0,0 @@ -import { ShadowdarkLightsourceTrackerTour } from "./tours/ShadowdarkTourLightsourceTracker.mjs"; -import { ShadowdarkPlayerRollingTour } from "./tours/ShadowdarkTourPlayerRolling.mjs"; -import { ShadowdarkTheLostCitadelTour } from "./tours/ShadowdarkTourTheLostCitadel.mjs"; -import { ShadowdarkMonsterImporterTour } from "./tours/ShadowdarkTourMonsterImporter.mjs"; - -export const ToursSD = { - register: () => { - const tours = { - "shadowdark-lightsource-tracker-tour": new ShadowdarkLightsourceTrackerTour(), - "shadowdark-player-rolls-tour": new ShadowdarkPlayerRollingTour(), - "shadowdark-the-lost-citadel-tour": new ShadowdarkTheLostCitadelTour(), - "shadowdark-Monster Importer-tour": new ShadowdarkMonsterImporterTour(), - }; - - for (const [label, tour] of Object.entries(tours)) { - game.tours.register( - "shadowdark", - label, - tour - ); - } - }, -}; diff --git a/system/src/tours/ShadowdarkTour.mjs b/system/src/tours/ShadowdarkTour.mjs deleted file mode 100644 index be7dc2f0e..000000000 --- a/system/src/tours/ShadowdarkTour.mjs +++ /dev/null @@ -1,73 +0,0 @@ -export default class ShadowdarkTour extends Tour { - // Overchange the "step" data with - // action: "click" or "scrollTo" - // target: CSS selector of the element to use for the action. - - /** - * Wait for an element to exist in the DOM, then resolve the promise - * - * @param {string} selector - CSS selector of the element to wait for - * @returns {Promise} - */ - async waitForElement(selector) { - return new Promise((resolve, reject) => { - const element = document.querySelector(selector); - if (element) { - resolve(); - return; - } - - const observer = new MutationObserver((mutations, observer) => { - document.querySelectorAll(selector).forEach(el => { - resolve(el); - observer.disconnect(); - }); - }); - - observer.observe(document.body, { - childList: true, - subtree: true, - }); - }); - } - - async _preStep() { - await super._preStep(); - await this.waitForElement(this.currentStep.selector); - } - - async _postStep() { - await super._postStep(); - if (this.stepIndex < 0 || !this.hasNext) return; - - if (!this.currentStep.action) return; - - if (this.triggerReset) { - this.triggerReset = false; - return; - } - - const target = this.currentStep.target ?? this.currentStep.selector; - // eslint-disable-next-line default-case - switch (this.currentStep.action) { - case "click": - document.querySelector(target).click(); - break; - - case "scrollTo": - document - .querySelector(target) - .scrollIntoView({ block: "start", inline: "nearest" }); - break; - } - } - - /** - * Detect when a reset is triggered and stop the actions in _postStep - */ - async reset() { - if (this.status !== "completed") this.triggerReset = true; - - await super.reset(); - } -} diff --git a/system/src/tours/ShadowdarkTourLightsourceTracker.mjs b/system/src/tours/ShadowdarkTourLightsourceTracker.mjs deleted file mode 100644 index fb01bf6ad..000000000 --- a/system/src/tours/ShadowdarkTourLightsourceTracker.mjs +++ /dev/null @@ -1,325 +0,0 @@ -/** - * @file Defines the Guided Tour for the Lightsource Tracker - */ -import ShadowdarkTour from "./ShadowdarkTour.mjs"; -import { delay } from "../testing/testUtils.mjs"; - -export class ShadowdarkLightsourceTrackerTour extends ShadowdarkTour { - constructor() { - super({ - title: "Lightsource Tracking: How it works", - description: - "Learn how the Lightsource tracker in Shadowdark RPG works.", - canBeResumed: false, - display: true, - steps: [ - { - id: "sd-lightsourcetracker-goto-tracker", - selector: ".shadowdark.light-tracker", - title: "Lightsource Tracker", - content: "

Welcome to the Lightsource Tracker UI.

\n

It helps the GM to keep track of the lightsources used on player-claimed actors in the game world.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-stay-tracker", - selector: ".shadowdark.light-tracker", - title: "Only for GM", - content: "

The Lightsource Tracker UI is only visible to GMs.

\n

Let's assign an actor to a user!

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-assigned-actor", - selector: ".shadowdark.light-tracker", - title: "Ms Fire-guide", - content: "

Ms Fire-guide has been claimed by The Tour Guide user and shows up on the tracker.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-open-actor-sheet", - selector: "a[data-tab='tab-inventory']", - title: "Ms Fire-guide", - content: "

Our player would proceed to the inventory.

", - action: "click", - }, - { - id: "sd-lightsourcetracker-activate-torch-icon", - selector: "a.item-toggle-light", - title: "Activate Torch", - content: "

By clicking on the activate lightsource button, the player lights their torch.

", - action: "click", - }, - { - id: "sd-lightsourcetracker-activate-torch-chat-message", - selector: "section.chat-sidebar", - title: "Torch Activated message", - content: "

Activating a light source sends a message to the chat log.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-activate-torch-tracker", - selector: ".shadowdark.light-tracker div.tourguideactor div.light-sources", - title: "Torch Activated Tracker", - content: "

... it also shows up in the Lightsource Tracker.

\n

If the player has a token in a scene, a pre-determined light-setting will activate, providing visibility.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-time-tracking", - selector: ".shadowdark.light-tracker div.tourguideactor div.light-sources div.time-remaining", - title: "Real Time", - content: "

With the lightsource activated, the time remaing starts ticking down as configured in the settings.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-goto-active", - selector: "p.status__value", - title: "Tracking Status", - content: "

The tracking status indicates if lightsources are being actively depleted.

\n

Depending on your settings, this will pause with the game.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-douse-torch", - selector: ".shadowdark.light-tracker div.tourguideactor div.light-sources div.light-controls", - title: "Douse individual lightsource", - content: "

The GM may douse individual light sources by clicking the flame.

\n

This will also turn the lights off on any tokens the player has.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-douse-all", - selector: ".shadowdark.light-tracker button.disable-all-lights", - title: "Douse all lightsources", - content: "

The GM may also turn off all the light sources, leaving them in darkness as the light from their tokens also deactivates.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-macro-sidebar", - selector: "a.item[data-tab=compendium]", - title: "Compendium", - content: "

In case you close the Lightsource Tracker UI by mistake and want to get it back, you need to head to the Macro compendium.

", - action: "click", - }, - { - id: "sd-lightsourcetracker-compendium-open", - selector: "div[id='compendium-shadowdark.macros'] .directory-header", - title: "Macros Compendium", - content: "

By opening the Macro Compendium, you can find various macros for Shadowdark RPG.

", - action: "ScrollTo", - }, - { - id: "sd-lightsourcetracker-macro", - selector: "li[data-document-id=VgfWt2xWzYJyVMbA]", - title: "Lightsource Tracker Macro", - content: "

This macro you can drag to the hotbar if you want a convenient way of accessing the Lightsource Tracker UI.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-goto-settings", - selector: "button[data-action=configure]", - title: "Go to Settings", - content: "

Let's take a look at the settings.

", - action: "click", - }, - { - id: "sd-lightsourcetracker-goto-settings-system", - selector: "a.category-tab[data-tab=system]", - title: "Go to Shadowdark Settings", - content: "

Go to system settings.

", - action: "click", - }, - { - id: "sd-lightsourcetracker-settings-track-light-sources", - selector: "input[name='shadowdark.trackLightSources']", - title: "Tracking Light Sources", - content: "

This setting toggles if light sources shall be tracked at all.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-settings-track-light-sources-open", - selector: "input[name='shadowdark.trackLightSourcesOpen']", - title: "Rendering the UI at start", - content: "

This setting toggles if the lightsource tracker UI shall be rendered for the GM when they log in.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-settings-track-inactive-users", - selector: "input[name='shadowdark.trackInactiveUserLightSources']", - title: "Tracking inactive users", - content: "

This setting toggles if the lightsource tracker shall track inactive users.

\n

If checked, the tracker will reduce time remaining even if the user is not logged on.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-settings-paused-with-game", - selector: "input[name='shadowdark.pauseLightTrackingWithGame']", - title: "Pausing with the Game", - content: "

This setting toggles if the lightsource tracker shall pause if you pause the game

\n

Unchecked will ignore pausing, for true Real Time tracking

\n

Checking will pause the timer if the GM pauses the game.

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-settings-track-intervall", - selector: "input[name='shadowdark.trackLightSourcesInterval']", - title: "Update frequency", - content: "

This slider will select how often the lightsources should be updated

\n

The number indicates the number of seconds between updates

", - action: "scrollTo", - }, - { - id: "sd-lightsourcetracker-end-tour", - selector: "#tours-management .window-title", - title: "Thank you!", - content: - "

Thank you! for following along, learning how the Lightsource Tracker UI for Shadowdark RPG works.

\n

For more information, see the other available tours.

", - action: "scrollTo", - }, - ], - }); - } - - async _setSettings(settings) { - for (const [key, value] of Object.entries(settings)) { - await game.settings.set("shadowdark", key, value); - } - } - - /** - * Override _preStep to wait for elements to exist in the DOM - */ - async _preStep() { - if (!game.settings.get("shadowdark", "trackLightSources")) { - ui.notifications.info( - game.i18n.localize("SHADOWDARK.tours.lightsource.notification.not_enabled"), - {permanent: true} - ); - await document.querySelector("button[data-action=configure]").click(); - await delay(200); - await document.querySelector("a.category-tab[data-tab=system]").click(); - await this.reset(); - return this.exit(); - } - - const MOCK_ACTOR_NAME = "Ms Fire-guide"; - const MOCK_USER = "The Tour Guide"; - const TOUR_SETTINGS = { - trackLightSourcesOpen: false, - trackInactiveUserLightSources: true, - pauseLightTrackingWithGame: true, - }; - - if (this.currentStep.id === "sd-lightsourcetracker-goto-tracker") { - // Clean-up previous tours if we have restarted - game.actors.filter(a => a.name === MOCK_ACTOR_NAME).forEach(a => a.delete()); - game.users.filter(u => u.name === MOCK_USER).forEach(a => a.delete()); - - // Setup an actor for the tour - const tourActor = await Actor.create({ - name: MOCK_ACTOR_NAME, - type: "Player", - img: "icons/magic/fire/elemental-fire-humanoid.webp", - ownership: { - default: 3, - }, - }); - - // Mock a user for the tour - await game.users.find(u => u.name === MOCK_USER)?.delete(); - await User.create({name: MOCK_USER}); - // character: tourActor._id - - // Add torch to character - const basicGearPack = game.packs.get("shadowdark.gear"); - const torchId = basicGearPack.index.find(i => i.name === "Torch")._id; - const torch = await basicGearPack.getDocument(torchId); - await tourActor.createEmbeddedDocuments("Item", [torch]); - - // Delay so the UI has time to catch up - await delay(200); - await game.shadowdark.lightSourceTracker.render(false); - - // Go to chat - document.querySelector('a[data-tab="chat"]').click(); - - // Check if the tour was started the last 10 minutes, if it was, then do not - // re-record the original settings. Otherwise, store current settings with runtime data. - if (!( - game.world.flags.tours - && game.world.flags.tours.lightSourceTracker - && (game.world.flags.tours.lightSourceTracker.lastRun - Date.now()) < 10*60*1000 - )) { - // Store data for the run - game.world.flags.tours = { - lightsourceTracker: { - lastRun: Date.now(), - schemaVersion: game.settings.get("shadowdark", "schemaVersion"), - originalSettings: { - trackLightSourcesOpen: game.settings.get("shadowdark", "trackLightSourcesOpen"), - trackInactiveUserLightSources: game.settings.get("shadowdark", "trackInactiveUserLightSources"), - pauseLightTrackingWithGame: game.settings.get("shadowdark", "pauseLightTrackingWithGame"), - }, - }, - }; - } - - // Set the temporary settings - await this._setSettings(TOUR_SETTINGS); - - // Render the lighttracker UI - if (!game.shadowdark.lightSourceTracker.rendered) { - await game.shadowdark.lightSourceTracker.toggleInterface(true); - } - } - - if (this.currentStep.id === "sd-lightsourcetracker-compendium-open") { - await game.packs.get("shadowdark.macros").render(true); - await delay(300); - } - - if (this.currentStep.id === "sd-lightsourcetracker-goto-settings") { - document.querySelector('a[data-tab="settings"]').click(); - } - - if (this.currentStep.id === "sd-lightsourcetracker-assigned-actor") { - const user = await game.users.find(u => u.name === MOCK_USER); - const actor = game.actors.find(a => a.name === MOCK_ACTOR_NAME); - await user.update({ - character: actor._id, - }); - } - - if ( - [ - "sd-lightsourcetracker-activate-torch-tracker", - "sd-lightsourcetracker-time-tracking", - "sd-lightsourcetracker-douse-torch", - "sd-lightsourcetracker-douse-all", - ].includes(this.currentStep.id) - ) { - // Add anchor to the div so we can find it later - $(`div.character-name:contains(${MOCK_ACTOR_NAME})`).parent().addClass("tourguideactor"); - } - - if (this.currentStep.id === "sd-lightsourcetracker-open-actor-sheet") { - const actor = game.actors.find(a => a.name === MOCK_ACTOR_NAME); - await actor.sheet.render(true); - } - - if (this.currentStep.id === "sd-lightsourcetracker-activate-torch-tracker") { - const actor = game.actors.find(a => a.name === MOCK_ACTOR_NAME); - await actor.sheet.close(); - } - - if (this.currentStep.id === "sd-lightsourcetracker-end-tour") { - Object.values(ui.windows).forEach(async w => { - await w.close(); - await delay(300); - }); - // Restore the settings - await this._setSettings(game.world.flags.tours.lightsourceTracker.originalSettings); - - await $("#settings button[data-action=tours]").click(); - await delay(200); - await document.querySelector("a.category-tab[data-tab=system]").click(); - - game.actors.filter(a => a.name === MOCK_ACTOR_NAME).forEach(a => a.delete()); - game.users.find(u => u.name === MOCK_USER).delete(); - } - - await super._preStep(); - } -} diff --git a/system/src/tours/ShadowdarkTourMonsterImporter.mjs b/system/src/tours/ShadowdarkTourMonsterImporter.mjs deleted file mode 100644 index dfae1e6e6..000000000 --- a/system/src/tours/ShadowdarkTourMonsterImporter.mjs +++ /dev/null @@ -1,102 +0,0 @@ -/** - * @file defines the tour for importing the lost citadel adventure - */ -import ShadowdarkTour from "./ShadowdarkTour.mjs"; -import { delay } from "../testing/testUtils.mjs"; - -export class ShadowdarkMonsterImporterTour extends ShadowdarkTour { - constructor() { - super({ - title: "Importing Monstesrs", - description: - "Learn how quickly import monstesr into Foundry for Shadowdark RPG.", - canBeResumed: false, - display: true, - steps: [ - { - id: "sd-monster-importer-start", - selector: ".compendium-sidebar", - title: "Compendium Tab", - content: "

The compendium tab contains collections that are included in the system. Let's open the macros compendium.

", - action: "scrollTo", - }, - { - id: "sd-monster-importer-compendium-open", - selector: "div[id='compendium-shadowdark.macros'] .directory-header", - title: "Macros Compendium", - content: "

This compendium includes many useful pre-built macros, such as the monster importer.

", - action: "scrollTo", - }, - { - id: "sd-monster-importer-compendium-marco", - selector: "li[data-document-id=sJTtbWtWigzBHf6N] h4 a", - title: "Open Monster Importer Macro", - content: "

You can drag this Macro to the hotbar if you want a convenient way of accessing it.

", - action: "click", - }, - { - id: "sd-monster-importer-Execute", - selector: ".execute", - title: "Run Monster Importer", - content: "

Click execute

", - action: "click", - }, - { - id: "sd-monster-importer-Text", - selector: ".app.window-app.monster-importer", - title: "Monster Importer Step 1", - content: "

Select and copy the monster's stat block text from your source document.

", - action: "scrollTo", - }, - { - id: "sd-monster-importer-Text", - selector: ".monster-importer textarea", - title: "Monster Importer Step 2", - content: "

Paste the monster text into the this text box. Make sure to sparate features with a blank line.

", - action: "scrollTo", - }, - { - id: "sd-monster-importer-import", - selector: "button.import", - title: "Monster Importer Step 3", - content: "

Click Import to create monters as an NPC actor under the actors tab.

", - action: "scrollTo", - }, - { - id: "sd-monster-importer-end-tour", - selector: "#tours-management .window-title", - title: "Thank you!", - content: - "

Thank you! for following along, learning how to Import Monsters for Shadowdark RPG.

\n

For more information, see the other available tours.

", - action: "scrollTo", - }, - ], - }); - } - - /** - * Override _preStep to wait for elements to exist in the DOM - */ - async _preStep() { - if (this.currentStep.id === "sd-monster-importer-start") { - // Go to compendium - document.querySelector('a[data-tab="compendium"]').click(); - } - - if (this.currentStep.id === "sd-monster-importer-compendium-open") { - await game.packs.get("shadowdark.macros").render(true); - await delay(300); - } - if (this.currentStep.id === "sd-monster-importer-end-tour") { - Object.values(ui.windows).forEach(async w => { - await w.close(); - await delay(300); - }); - await $("#settings button[data-action=tours]").click(); - await delay(200); - await document.querySelector("a.category-tab[data-tab=system]").click(); - } - - await super._preStep(); - } -} diff --git a/system/src/tours/ShadowdarkTourPlayerRolling.mjs b/system/src/tours/ShadowdarkTourPlayerRolling.mjs deleted file mode 100644 index ac6f32afb..000000000 --- a/system/src/tours/ShadowdarkTourPlayerRolling.mjs +++ /dev/null @@ -1,398 +0,0 @@ -/** - * @file defines the system user guide tour - */ -import ShadowdarkTour from "./ShadowdarkTour.mjs"; -import { closeDialogs, delay, waitForInput } from "../testing/testUtils.mjs"; - -export class ShadowdarkPlayerRollingTour extends ShadowdarkTour { - constructor() { - super({ - title: "Players: Rolling dice from the character sheet", - description: - "Learn how rolling attacks, spells, etc. for a player works in Shadowdark RPG.", - canBeResumed: false, - display: true, - steps: [ - { - id: "sd-playerroll-start", - selector: ".shadowdark.player", - title: "Player Character Sheet: Rolling Dice", - content: "

Welcome to the Player Character Sheet.

\n

Here you will be able to find and modify your character, including adding spells and items, and especially; rolling dice.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-abilities", - selector: ".ability[data-ability=str]", - title: "Ability Scores", - content: "

Clicking on an ability score will prompt you with a roll dialog for rolling the associated ability check.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-abilities-click", - selector: ".ability[data-ability=str] .rollable", - title: "Ability Scores: Rolling", - content: "

Lets roll a strength ability score.

", - action: "click", - }, - { - id: "sd-playerroll-ability-roll-ability-window", - selector: ".dialog", - title: "Ability Check Roll", - content: "

This dialog asks for your input and before it rolls the dice..

", - action: "scrollTo", - }, - { - id: "sd-playerroll-ability-roll-ability-bonus", - selector: ".shadowdark-dialog input[name=ability-bonus]", - title: "Ability Check: Ability Bonus", - content: "

This field contains your ability modifier and will be added to the roll.

\n

The field is already filled out, but you could change it before the roll executes.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-ability-roll-mode", - selector: ".shadowdark-dialog select[name=rollMode]", - title: "Ability Check: Roll Mode", - content: "

This field allows you to select how the roll will be displayed in the chat.

\n

Public Roll shows the result to everyone.

\n

Private GM Roll send the roll to the GM, but you can also see the result.

\n

Blind GM Roll sends the roll to the GM and hides it from you.

\n

Self Roll only shows the roll to yourself, not even the GM can see it.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-ability-roll-mode-advantage", - selector: "button.advantage", - title: "Ability Check: Advantage", - content: "

This button rolls with advantage (roll two D20 and keep the highest).

\n

This will light up green if you have a talent that gives you advantage.

\n

The roll will not be automatically rolled with advantage however, as this is left to the conversation between player and GM

", - action: "scrollTo", - }, - { - id: "sd-playerroll-ability-roll-mode-normal", - selector: "button.normal", - title: "Ability Check: Normal", - content: "

This button rolls normally.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-ability-roll-mode-disadvantage", - selector: "button.disadvantage", - title: "Ability Check: Disadvantage", - content: "

This button rolls with disadvantage (roll two D20 and keep the lowest).

", - action: "click", - }, - { - id: "sd-playerroll-ability-roll-message", - selector: ".message:last-child", - title: "Rolling to chat", - content: "

Clicking on one of the buttons in the previous dialog will roll to chat.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-hp", - selector: "label.hp", - title: "Rolling HD", - content: "

Clicking on an HP label will roll your hit dice.

", - action: "click", - }, - { - id: "sd-playerroll-hp-dialog", - selector: ".dialog", - title: "Rolling HD Dialog", - content: "

This is the dialog for rolling HP / HD.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-hp-normal", - selector: "button.normal", - title: "Rolling HD: Normal", - content: "

Let's roll a normal roll this time.

", - action: "click", - }, - { - id: "sd-playerroll-hp-roll-message", - selector: ".message:last-child", - title: "HP Chat Card", - content: "

The HP card is slightly different, as it has a button.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-hp-roll-message-button", - selector: ".message:last-child button[data-action='apply-hp-to-max']", - title: "HP Chat Card: Button", - content: "

The HP card has a button that allows you to add the roll result to your maximum HP if you want to.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-items-navigate", - selector: "a.navigation-tab[data-tab='tab-inventory']", - title: "Items", - content: "

Lets look at how you roll items.

", - action: "click", - }, - { - id: "sd-playerroll-items-list", - selector: ".inventory-list", - title: "List of Items", - content: "

In this example, the Player has access to a Greataxe.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-items-rollable", - selector: ".item-image[style='background-image: url(icons/weapons/axes/axe-broad-engraved-chipped-blue.webp)']", - title: "Rolling Weapon", - content: "

Clicking the icon will create a chat message with information about the item.

", - action: "click", - }, - { - id: "sd-playerroll-items-chatcard", - selector: ".message:last-child button[data-action=roll-attack]", - title: "Roll weapon attack", - content: "

Clicking on the button on the chat card will open the roll weapon dialog", - action: "click", - }, - { - id: "sd-playerroll-items-dialog", - selector: ".dialog", - title: "Roll Weapon Dialog", - content: "

The Roll Weapon dialog has more options than the previous ones.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-items-dialog-item-bonus", - selector: "input[name='item-bonus']", - title: "Item Bonus", - content: "

If the item is magical, or of extraordinary quality, the bonus will be added here in this field.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-items-dialog-item-bonus", - selector: "input[name='weapon-backstab']", - title: "Backstab", - content: "

If you are playing a Thief (like our example), you will have the option to backstab and adding the extra dice you get with that.

\n

If you have talents that add dice to these rolls, it adds them automatically

", - action: "click", - }, - { - id: "sd-playerroll-items-roll", - selector: "button.advantage", - title: "Roll", - content: "

Let's roll with advantage

", - action: "click", - }, - { - id: "sd-playerroll-items-roll-message", - selector: ".message:last-child", - title: "Weapon attack roll", - content: "

The weapon roll is displayed to chat.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-items-roll-message-damage", - selector: ".message:last-child .card-damage-rolls", - title: "Damage rolls", - content: "

The weapon was a versatile weapon, and will immediately roll both damage dice simultaneously to speed up combat.

\n

As you can see, the additional dice are added as it was a backstab!

", - action: "scrollTo", - }, - { - id: "sd-playerroll-items-item", - selector: "a.item-toggle-equipped", - title: "Equipping the weapon", - content: "

Clicking the gray shirt will equip the weapon (or armor).

\n

This generates attacks on the Abilities tab.

", - action: "click", - }, - { - id: "sd-playerroll-items-abilities-navigate", - selector: "a.navigation-tab[data-tab='tab-abilities']", - title: "Items", - content: "

Lets look at how you roll items.

", - action: "click", - }, - { - id: "sd-playerroll-items-attack", - selector: "li.attack-item", - title: "Attack rolls", - content: "

These attacks are automatically created when you equip a weapon

\n

Clicking on them will give you the roll dialog.

", - action: "click", - }, - { - id: "sd-playerroll-items-attack-normal", - selector: "button.normal", - title: "Attack normally", - content: "

Let's just roll normally this time.

", - action: "click", - }, - { - id: "sd-playerroll-items-attack-roll-message", - selector: ".message:last-child", - title: "Weapon attack to chat", - content: "

The weapon roll is once again displayed to chat.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-back-to-actor", - selector: ".sheet.player", - title: "Spells", - content: "

Lets transform our hero into a Wizard and look at how you roll spells!

", - action: "scrollTo", - }, - { - id: "sd-playerroll-spells-navigate", - selector: ".sheet.player a.navigation-tab[data-tab='tab-spells']", - title: "Spells: Tab", - content: "

Lets look at the spells tab.

", - action: "click", - }, - { - id: "sd-playerroll-spells-spell-list", - selector: ".spells-body", - title: "Spells", - content: "

In this example, the Player has access to Magic Missile.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-spells-spell-available", - selector: "a.item-control.toggle-lost", - title: "Spell Available?", - content: "

This check-mark means the spell is available.

\n

A miscast will automatically lose the spell for the day, turning the check-mark to a cross

", - action: "scrollTo", - }, - { - id: "sd-playerroll-spells-spell-roll", - selector: "a.item-control.cast-spell", - title: "Cast Spell", - content: "

Clicking the wand icon will give you a cast dialog and cast the spell.

", - action: "click", - }, - { - id: "sd-playerroll-hp-dialog", - selector: ".dialog", - title: "Rolling Spell Dialog", - content: "

This is the dialog for rolling a spell.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-spells-spell-roll-talent", - selector: "input[name='talent-bonus']", - title: "Talent Bonus", - content: "

If you are lucky and get +1 to spell checks through talents, the bonuses will be added and displayed here.

", - action: "click", - }, - { - id: "sd-playerroll-spells-spell-roll-cast", - selector: "button.disadvantage", - title: "Cast Spell with Disadadvantage", - content: "

Let's try a roll at disadvantage!

", - action: "click", - }, - { - id: "sd-playerroll-spells-roll-message", - selector: ".message:last-child", - title: "Spell Chat Card", - content: "

The Spell chat cards contains the information for the spell. In some cases it includes a clickable roll for damage rolls e.g.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-spells-roll-message-damage", - selector: ".message:last-child .inline-roll", - title: "Rollable button", - content: "

For Magic Missile we get the chance to roll a 1d4 damage.

", - action: "scrollTo", - }, - - { - id: "sd-playerroll-spells-miscast", - selector: ".player a.item-control.toggle-lost", - title: "Lost Spell", - content: "

If a spell is lost due to failed spell cast, clicking the cross will restore it.

", - action: "scrollTo", - }, - { - id: "sd-playerroll-end-tour", - selector: "#tours-management .window-title", - title: "Thank you!", - content: - "

Thank you! for following along, learning how to roll from Player character sheet for Shadowdark RPG.

\n

For more information, see the other available tours.

", - action: "scrollTo", - }, - ], - }); - } - - async _setSettings(settings) { - for (const [key, value] of Object.entries(settings)) { - await game.settings.set("shadowdark", key, value); - } - } - - /** - * Override _preStep to wait for elements to exist in the DOM - */ - async _preStep() { - const MOCK_ACTOR_NAME = "Tour Player"; - - if (this.currentStep.id === "sd-playerroll-start") { - // Clean-up previous tours if we have restarted - game.actors.filter(a => a.name === MOCK_ACTOR_NAME).forEach(a => a.delete()); - - // Go to chat - document.querySelector('a[data-tab="chat"]').click(); - - // Setup an actor for the tour - const tourActor = await Actor.create({ - name: MOCK_ACTOR_NAME, - type: "Player", - system: { class: "Compendium.shadowdark.classes.Item.C6wkCa2w5dlgSq7f" }, // thief class - ownership: { default: 3 }, - img: "systems/shadowdark/assets/quickstart/pregens/Zaldini_the_Red_portrait.webp", - }); - - // Add items to character - const items = []; - const talentPack = game.packs.get("shadowdark.talents"); - const talentId = talentPack.index.find(i => i.name === "Backstab")._id; - items.push(await talentPack.getDocument(talentId)); - - const weaponsPack = game.packs.get("shadowdark.gear"); - const weaponId = weaponsPack.index.find(i => i.name === "Greataxe")._id; - items.push(await weaponsPack.getDocument(weaponId)); - - await tourActor.createEmbeddedDocuments("Item", items); - - // Delay so the UI has time to catch up - await tourActor.sheet.render(true); - await delay(200); - } - - if (this.currentStep.id === "sd-playerroll-back-to-actor") { - let tourActor = await game.actors.find(actor => actor.name === MOCK_ACTOR_NAME); - - tourActor = await tourActor.update({ - "system.class": "Compendium.shadowdark.classes.Item.035nuVkU9q2wtMPs", // wizard class - "system.spellcastingAbility": "int", - }); - - const items = []; - const spellsPack = game.packs.get("shadowdark.spells"); - const spellId = spellsPack.index.find(i => i.name === "Magic Missile")._id; - items.push(await spellsPack.getDocument(spellId)); - - await tourActor.createEmbeddedDocuments("Item", items); - } - - if (this.currentStep.selector.includes(".message")) { - await waitForInput(); - } - - if (this.currentStep === "sd-playerroll-hp") { - await closeDialogs(); - } - - if (this.currentStep.id === "sd-playerroll-end-tour") { - Object.values(ui.windows).forEach(async w => { - await w.close(); - await delay(300); - }); - await $("#settings button[data-action=tours]").click(); - await delay(200); - await document.querySelector("a.category-tab[data-tab=system]").click(); - game.actors.filter(a => a.name === MOCK_ACTOR_NAME).forEach(a => a.delete()); - } - - await super._preStep(); - } -} diff --git a/system/src/tours/ShadowdarkTourTheLostCitadel.mjs b/system/src/tours/ShadowdarkTourTheLostCitadel.mjs deleted file mode 100644 index 570bb02d3..000000000 --- a/system/src/tours/ShadowdarkTourTheLostCitadel.mjs +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @file defines the tour for importing the lost citadel adventure - */ -import { delay } from "../testing/testUtils.mjs"; -import ShadowdarkTour from "./ShadowdarkTour.mjs"; - -export class ShadowdarkTheLostCitadelTour extends ShadowdarkTour { - constructor() { - super({ - title: "The Lost Citadel of the Scarlet Minotaur", - description: - "Learn how import the Quickstart adventure into Foundry for Shadowdark RPG.", - canBeResumed: false, - display: true, - steps: [ - { - id: "sd-tlc-start", - selector: ".compendium-sidebar", - title: "Compendium", - content: "

The compendium tab contains various collections that are included in the system (as well as modules, and the world).

", - action: "scrollTo", - }, - { - id: "sd-tlc-compendium-open", - selector: "div[id='compendium-shadowdark.quickstart-adventures'] .directory-header", - title: "Quickstart folder", - content: "

Open the Quick Start Adventures compendium.

", - action: "scrollTo", - }, - { - id: "sd-tlc-compendium-adventure", - selector: "li.adventure h4", - title: "Adventure", - content: "

The system includes the introductory adventure The Lost Citadel of the Scarlet Minotaur from the Quickstarter ruleset.

", - action: "click", - }, - { - id: "sd-tlc-compendium-importer", - selector: ".adventure-importer", - title: "Adventure Importer", - content: "

The importer allows to easily import the adventure.

", - action: "scrollTo", - }, - { - id: "sd-tlc-compendium-importer-import", - selector: ".adventure-footer button", - title: "Import Adventure", - content: "

Clicking the Import Adventure button will import the adventure to your world, and you are ready to go.

", - action: "scrollTo", - }, - { - id: "sd-tlc-end-tour", - selector: "#tours-management .window-title", - title: "Thank you!", - content: - "

Thank you! for following along, learning how to import the Quickstart adventure into Foundry for Shadowdark RPG.

\n

For more information, see the other available tours.

", - action: "scrollTo", - }, - ], - }); - } - - /** - * Override _preStep to wait for elements to exist in the DOM - */ - async _preStep() { - if (this.currentStep.id === "sd-tlc-start") { - // Go to compendium - document.querySelector('a[data-tab="compendium"]').click(); - } - if (this.currentStep.id === "sd-tlc-compendium-open") { - await game.packs.get("shadowdark.quickstart-adventures").render(true); - await delay(300); - } - if (this.currentStep.id === "sd-tlc-compendium-importer") { - await delay(300); - } - - if (this.currentStep.id === "sd-tlc-end-tour") { - Object.values(ui.windows).forEach(async w => { - await w.close(); - await delay(300); - }); - await $("#settings button[data-action=tours]").click(); - await delay(200); - await document.querySelector("a.category-tab[data-tab=system]").click(); - } - - await super._preStep(); - } -} diff --git a/system/src/tours/_module.mjs b/system/src/tours/_module.mjs deleted file mode 100644 index cd63618f0..000000000 --- a/system/src/tours/_module.mjs +++ /dev/null @@ -1 +0,0 @@ -export {default as ShadowdarkLightsourceTrackerTour} from "./ShadowdarkTourLightsourceTracker.mjs"; diff --git a/system/templates/chat/welcome-message.hbs b/system/templates/chat/welcome-message.hbs index efd4c0b6f..eef60aad9 100644 --- a/system/templates/chat/welcome-message.hbs +++ b/system/templates/chat/welcome-message.hbs @@ -4,11 +4,6 @@

{{title}}

-

{{localize "SHADOWDARK.chat.welcome_message.intro"}}

-

{{localize "SHADOWDARK.chat.welcome_message.issues"}}