Skip to content

Commit

Permalink
[#102] Context menu option for scrying on a Cards. (#103)
Browse files Browse the repository at this point in the history
  • Loading branch information
krbz999 authored Jul 20, 2024
1 parent 6e0e8bf commit 0e79f6b
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 3 deletions.
2 changes: 2 additions & 0 deletions ccm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ Hooks.on("deleteCard", hooks.deleteCard);
Hooks.on("deleteCards", hooks.deleteCard);

Hooks.on("createScene", hooks.createScene);

Hooks.on("getCardsDirectoryEntryContext", hooks.addCardsDirectoryOptions);
3 changes: 3 additions & 0 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@
"Unnamed": "Unnamed ({idx})",
"DeleteFacePrompt": "Delete this face? This action cannot be undone.",
"DeleteFaceTitle": "Delete Face",
"ScryingContext": "Scry",
"ScryingTitle": "Scrying on: {name}",
"ScryingMessage": "{name} is scrying on {number} cards in {deck}.",
"ScryingMessageReorder": "{name} shuffled the {number} revealed cards back into {deck}.",
"ScryingShuffleAndReplace": "Shuffle and Replace",
"ScryPromptHint": "Choose the amount of cards to scry on from this set of cards.",
"ScryPromptLabel": "Amount",

"CardType": "Type",
"CardSuit": "Suit",
Expand Down
1 change: 1 addition & 0 deletions src/module/api/scry.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class ScryDialog extends HandlebarsApplicationMixin(ApplicationV2) {
rejectClose: false,
position: {
width: 600,
top: 100,
height: "auto"
},
window: {
Expand Down
98 changes: 95 additions & 3 deletions src/module/hooks.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,18 @@ export function init() {
Hooks.callAll("CCMInit");
}

/* -------------------------------------------------- */

/**
* Run on Foundry ready
*/
export function ready() {
console.log("Complete Card Management | Ready");
}

/****************
* Canvas Hooks
****************/
/* -------------------------------------------------- */
/* Canvas hooks */
/* -------------------------------------------------- */

/**
* Handles drop data
Expand All @@ -84,6 +86,8 @@ export function dropCanvasData(canvas, data) {
}
}

/* -------------------------------------------------- */

/**
*
* @param {Canvas} canvas - The Game Canvas
Expand All @@ -103,6 +107,8 @@ async function handleCardDrop(canvas, data) {
api.placeCard(card, data);
}

/* -------------------------------------------------- */

/**
*
* @param {Canvas} canvas - The Game Canvas
Expand All @@ -119,6 +125,8 @@ async function handleCardStackDrop(canvas, data) {
api.placeCard(cards, data);
}

/* -------------------------------------------------- */

/**
* A hook event that fires when Cards are passed from one stack to another.
* @event passCards
Expand Down Expand Up @@ -157,6 +165,8 @@ export function passCards(origin, destination, context) {
);
}

/* -------------------------------------------------- */

/**
* A hook event that fires for every embedded Document type after conclusion of a creation workflow.
* Substitute the Document name in the hook event to target a specific type, for example "createToken".
Expand All @@ -177,6 +187,8 @@ export async function createCard(card, options, userId) {
}
}

/* -------------------------------------------------- */

/**
* A hook event that fires for every Document type after conclusion of an update workflow.
* Substitute the Document name in the hook event to target a specific Document type, for example "updateActor".
Expand Down Expand Up @@ -212,6 +224,8 @@ export async function updateCard(card, changed, options, userId) {
}
}

/* -------------------------------------------------- */

/**
* A hook event that fires for every Document type after conclusion of an deletion workflow.
* Substitute the Document name in the hook event to target a specific Document type, for example "deleteActor".
Expand All @@ -229,6 +243,8 @@ export async function deleteCard(card, options, userId) {
}
}

/* -------------------------------------------------- */

/**
* Hook event method for adding cards layer controls.
* @param {SceneControl[]} controls
Expand Down Expand Up @@ -266,6 +282,8 @@ export function getSceneControlButtons(controls) {
});
}

/* -------------------------------------------------- */

/**
* A hook called when the canvas HUD is rendered during `Canvas#initialize`
* @param {HeadsUpDisplay} app - The HeadsUpDisplay application
Expand All @@ -280,6 +298,8 @@ export function renderHeadsUpDisplay(app, [html], context) {
html.appendChild(cardHudTemplate);
}

/* -------------------------------------------------- */

/**
* A hook event that fires for every embedded Document type after conclusion of a creation workflow.
* Substitute the Document name in the hook event to target a specific type, for example "createToken".
Expand Down Expand Up @@ -323,3 +343,75 @@ export async function createScene(scene, options, userId) {
await game.cards.get(id).updateEmbeddedDocuments("Card", updates);
}
}

/* -------------------------------------------------- */

/**
* Add additional context options to cards in cards directory.
* @param {HTMLElement} html The sidebar html.
* @param {object[]} options The array of context menu options.
*/
export function addCardsDirectoryOptions(html, options) {
options.push({
name: "CCM.CardSheet.ScryingContext",
icon: "<i class='fa-solid fa-eye'></i>",
callback: async ([li]) => {
const id = li.dataset.documentId;
const cards = game.cards.get(id);
const data = await promptAmount(cards);
if (!data) return;
ccm.api.scry(cards, {amount: data.amount, how: data.mode});
}
});
}

/* -------------------------------------------------- */

/**
* Create a prompt for the user to select how many cards they want to have revealed, and how.
* @param {Cards} cards The deck, hand, or pile of cards.
* @returns {Promise<object|null|void>} A promise that resolves to the number of cards and how to draw.
*/
async function promptAmount(cards) {
const max = (cards.type === "deck") ? cards.availableCards.length : cards.cards.size;
if (!max) {
ui.notifications.warn(game.i18n.format("CCM.Warning.NoCardsAvailable", {
type: game.i18n.localize(CONFIG.Cards.typeLabels[cards.type])
}));
return;
}

const rangePicker = new foundry.data.fields.NumberField({
label: "CCM.CardSheet.ScryPromptLabel",
hint: "CCM.CardSheet.ScryPromptHint"
}).toFormGroup({localize: true}, {
value: 1, step: 1, min: 1, max: max, name: "amount"
}).outerHTML;

const drawMode = new foundry.data.fields.NumberField({
label: "CARDS.DrawMode",
choices: {
[CONST.CARD_DRAW_MODES.TOP]: "CARDS.DrawModeTop",
[CONST.CARD_DRAW_MODES.BOTTOM]: "CARDS.DrawModeBottom"
}
}).toFormGroup({localize: true}, {
value: CONST.CARD_DRAW_MODES.TOP, blank: false, name: "mode", localize: true
}).outerHTML;

const title = game.i18n.format("CCM.CardSheet.ScryingTitle", {name: cards.name});

const data = await foundry.applications.api.DialogV2.prompt({
modal: true,
rejectClose: false,
content: `<fieldset>${rangePicker}${drawMode}</fieldset>`,
window: {title: title, icon: "fa-solid fa-eye"},
position: {width: 400},
ok: {
callback: (event, button, html) => {
const {amount, mode} = new FormDataExtended(button.form).object;
return {amount, mode};
}
}
});
return data;
}

0 comments on commit 0e79f6b

Please sign in to comment.