Merge branch 'item-macros' into 'master'
Add functionality to create item macros via drag & drop Closes #80 See merge request dungeonslayers/ds4!91
This commit is contained in:
commit
79355b6e9c
19 changed files with 265 additions and 125 deletions
|
@ -45,7 +45,7 @@
|
||||||
"postinstall": "husky install"
|
"postinstall": "husky install"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@league-of-foundry-developers/foundry-vtt-types": "^0.7.9-1",
|
"@league-of-foundry-developers/foundry-vtt-types": "^0.7.9-3",
|
||||||
"@types/fs-extra": "^9.0.8",
|
"@types/fs-extra": "^9.0.8",
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^26.0.20",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.16.1",
|
"@typescript-eslint/eslint-plugin": "^4.16.1",
|
||||||
|
|
|
@ -193,7 +193,12 @@
|
||||||
"DS4.ErrorRollingForItemTypeNotPossible": "Würfeln ist für Items vom Typ '{type}' nicht möglich.",
|
"DS4.ErrorRollingForItemTypeNotPossible": "Würfeln ist für Items vom Typ '{type}' nicht möglich.",
|
||||||
"DS4.ErrorWrongItemType": "Ein Item vom Type '{expectedType}' wurde erwartet aber das Item '{name}' ({id}) ist vom Typ '{actualType}'.",
|
"DS4.ErrorWrongItemType": "Ein Item vom Type '{expectedType}' wurde erwartet aber das Item '{name}' ({id}) ist vom Typ '{actualType}'.",
|
||||||
"DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffarten: {expectedTypes}",
|
"DS4.ErrorUnexpectedAttackType": "Unerwartete Angriffsart '{actualType}', erwartete Angriffarten: {expectedTypes}",
|
||||||
"DS4.ErrorItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.",
|
"DS4.ErrorCanvasIsNotInitialized": "Canvas ist noch nicht initialisiert.",
|
||||||
|
"DS4.WarningItemMustBeEquippedToBeRolled": "Um für das Item '{name}' ({id}) vom Typ '{type}' zu würfeln, muss es ausgerüstet sein.",
|
||||||
|
"DS4.WarningMustControlActorToUseRollItemMacro": "Um ein ein Item Würfel Makro zu nutzen muss ein Aktor kontolliert werden.",
|
||||||
|
"DS4.WarningControlledActorDoesNotHaveItem": "Der kontrollierte Aktor '{actorName}' ({actorId}) hat kein Item mit der ID '{itemId}'.",
|
||||||
|
"DS4.WarningItemIsNotRollable": "Für das Item '{name}' ({id}) vom Typ '{type}' kann nicht gewürfelt werden.",
|
||||||
|
"DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Makros können nur für besessene Items angelegt werden.",
|
||||||
"DS4.InfoManuallyEnterSpellBonus": "Der korrekte Wert für den Zauberbonus '{spellBonus}' des Zaubers '{name}' musss manuell angegeben werden.",
|
"DS4.InfoManuallyEnterSpellBonus": "Der korrekte Wert für den Zauberbonus '{spellBonus}' des Zaubers '{name}' musss manuell angegeben werden.",
|
||||||
"DS4.InfoSystemUpdateStart": "Aktualisiere DS4 System von Migrationsversion {currentVersion} auf {targetVersion}. Bitte haben Sie etwas Geduld, schließen Sie nicht das Spiel und fahren Sie nicht den Server herunter.",
|
"DS4.InfoSystemUpdateStart": "Aktualisiere DS4 System von Migrationsversion {currentVersion} auf {targetVersion}. Bitte haben Sie etwas Geduld, schließen Sie nicht das Spiel und fahren Sie nicht den Server herunter.",
|
||||||
"DS4.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion} erfolgreich!",
|
"DS4.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion} erfolgreich!",
|
||||||
|
|
|
@ -193,8 +193,13 @@
|
||||||
"DS4.ErrorRollingForItemTypeNotPossible": "Rolling is not possible for items of type '{type}'.",
|
"DS4.ErrorRollingForItemTypeNotPossible": "Rolling is not possible for items of type '{type}'.",
|
||||||
"DS4.ErrorWrongItemType": "Expected an item of type '{expectedType}' but item '{name}' ({id}) is of type '{actualType}'.",
|
"DS4.ErrorWrongItemType": "Expected an item of type '{expectedType}' but item '{name}' ({id}) is of type '{actualType}'.",
|
||||||
"DS4.ErrorUnexpectedAttackType": "Unexpected attack type '{actualType}', expected it to be one of: {expectedTypes}",
|
"DS4.ErrorUnexpectedAttackType": "Unexpected attack type '{actualType}', expected it to be one of: {expectedTypes}",
|
||||||
"DS4.ErrorItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.",
|
"DS4.ErrorCanvasIsNotInitialized": "Canvas is not initialized yet.",
|
||||||
"DS4.InfoManuallyEnterSpellBonus": "The correct value of the spell bons '{spellBonus}' of the spell '{name}' needs to be entered by manually.",
|
"DS4.WarningItemMustBeEquippedToBeRolled": "To roll for item '{name}' ({id}) of type '{type}', it needs to be equipped.",
|
||||||
|
"DS4.WarningMustControlActorToUseRollItemMacro": "You must control an actor to be able to use a roll item macro.",
|
||||||
|
"DS4.WarningControlledActorDoesNotHaveItem": "Your controlled actor '{actorName}' ({actorId}) does not have any item with the id '{itemId}'.",
|
||||||
|
"DS4.WarningItemIsNotRollable": "Item '{name}' ({id}) of type '{type}' is not rollable.",
|
||||||
|
"DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems": "Macros can only be created for owned items.",
|
||||||
|
"DS4.InfoManuallyEnterSpellBonus": "The correct value of the spell bonus '{spellBonus}' of the spell '{name}' needs to be entered by manually.",
|
||||||
"DS4.InfoSystemUpdateStart": "Migrating DS4 system from migration version {currentVersion} to {targetVersion}. Please be patient and do not close your game or shut down your server.",
|
"DS4.InfoSystemUpdateStart": "Migrating DS4 system from migration version {currentVersion} to {targetVersion}. Please be patient and do not close your game or shut down your server.",
|
||||||
"DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from migration version {currentVersion} to {targetVersion} successful!",
|
"DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from migration version {currentVersion} to {targetVersion} successful!",
|
||||||
"DS4.UnitRounds": "Rounds",
|
"DS4.UnitRounds": "Rounds",
|
||||||
|
|
|
@ -1,102 +1,3 @@
|
||||||
import { DS4Actor } from "./actor/actor";
|
import registerForHooks from "./hooks/hooks";
|
||||||
import { DS4CharacterActorSheet } from "./actor/sheets/character-sheet";
|
|
||||||
import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet";
|
|
||||||
import { DS4 } from "./config";
|
|
||||||
import registerHandlebarsHelpers from "./handlebars/handlebars-helpers";
|
|
||||||
import registerHandlebarsPartials from "./handlebars/handlebars-partials";
|
|
||||||
import { DS4Item } from "./item/item";
|
|
||||||
import { DS4ItemSheet } from "./item/item-sheet";
|
|
||||||
import { migration } from "./migrations";
|
|
||||||
import { DS4Check } from "./rolls/check";
|
|
||||||
import { createCheckRoll } from "./rolls/check-factory";
|
|
||||||
import { DS4Roll } from "./rolls/roll";
|
|
||||||
import registerSlayingDiceModifier from "./rolls/slaying-dice-modifier";
|
|
||||||
import { registerSystemSettings } from "./settings";
|
|
||||||
|
|
||||||
Hooks.once("init", async () => {
|
registerForHooks();
|
||||||
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
|
||||||
|
|
||||||
game.ds4 = {
|
|
||||||
DS4Actor,
|
|
||||||
DS4Item,
|
|
||||||
DS4,
|
|
||||||
createCheckRoll,
|
|
||||||
migration,
|
|
||||||
};
|
|
||||||
|
|
||||||
CONFIG.DS4 = DS4;
|
|
||||||
|
|
||||||
CONFIG.Actor.entityClass = DS4Actor;
|
|
||||||
CONFIG.Item.entityClass = DS4Item;
|
|
||||||
|
|
||||||
CONFIG.Actor.typeLabels = DS4.i18n.actorTypes;
|
|
||||||
CONFIG.Item.typeLabels = DS4.i18n.itemTypes;
|
|
||||||
|
|
||||||
CONFIG.Dice.types.push(DS4Check);
|
|
||||||
CONFIG.Dice.terms.s = DS4Check;
|
|
||||||
|
|
||||||
CONFIG.Dice.rolls.unshift(DS4Roll);
|
|
||||||
|
|
||||||
registerSlayingDiceModifier();
|
|
||||||
|
|
||||||
registerSystemSettings();
|
|
||||||
|
|
||||||
Actors.unregisterSheet("core", ActorSheet);
|
|
||||||
Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true });
|
|
||||||
Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
|
|
||||||
Items.unregisterSheet("core", ItemSheet);
|
|
||||||
Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
|
|
||||||
|
|
||||||
await registerHandlebarsPartials();
|
|
||||||
registerHandlebarsHelpers();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
|
||||||
*/
|
|
||||||
Hooks.once("setup", () => {
|
|
||||||
localizeAndSortConfigObjects();
|
|
||||||
});
|
|
||||||
|
|
||||||
Hooks.once("ready", () => {
|
|
||||||
migration.migrate();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the text of input elements in given sheets via onfocus listener.
|
|
||||||
* The hook names are of the form "render"+sheet_superclassname and are called within
|
|
||||||
* the render() method of the foundry Application class.
|
|
||||||
* Note: The render hooks of all classes in the class hierarchy are called,
|
|
||||||
* so e.g. for a Dialog, both "renderDialog" and "renderApplication" are called
|
|
||||||
* (in this order).
|
|
||||||
*/
|
|
||||||
["renderApplication", "renderActorSheet", "renderItemSheet"].forEach((hookName: string) => {
|
|
||||||
Hooks.on(hookName, (app: Dialog, html: JQueryStatic) => {
|
|
||||||
$(html)
|
|
||||||
.find("input")
|
|
||||||
.on("focus", (ev: JQuery.FocusEvent<HTMLInputElement>) => {
|
|
||||||
ev.currentTarget.select();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Localizes all objects in {@link DS4.i18n} and sorts them unless they are explicitly excluded.
|
|
||||||
*/
|
|
||||||
function localizeAndSortConfigObjects() {
|
|
||||||
const noSort = ["attributes", "traits", "combatValues", "creatureSizeCategories"];
|
|
||||||
|
|
||||||
const localizeObject = <T extends { [s: string]: string }>(obj: T, sort = true): T => {
|
|
||||||
const localized = Object.entries(obj).map(([key, value]) => {
|
|
||||||
return [key, game.i18n.localize(value)];
|
|
||||||
});
|
|
||||||
if (sort) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
|
||||||
return Object.fromEntries(localized);
|
|
||||||
};
|
|
||||||
|
|
||||||
DS4.i18n = Object.fromEntries(
|
|
||||||
Object.entries(DS4.i18n).map(([key, value]) => {
|
|
||||||
return [key, localizeObject(value, !noSort.includes(key))];
|
|
||||||
}),
|
|
||||||
) as typeof DS4.i18n;
|
|
||||||
}
|
|
||||||
|
|
6
src/module/helpers.ts
Normal file
6
src/module/helpers.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export function getCanvas(): Canvas {
|
||||||
|
if (!(canvas instanceof Canvas) || !canvas.ready) {
|
||||||
|
throw new Error(game.i18n.localize("DS4.ErrorCanvasIsNotInitialized"));
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
}
|
13
src/module/hooks/hooks.ts
Normal file
13
src/module/hooks/hooks.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import registerForHotbarDropHook from "./hotbar-drop";
|
||||||
|
import registerForInitHook from "./init";
|
||||||
|
import registerForReadyHook from "./ready";
|
||||||
|
import registerForRenderHooks from "./render";
|
||||||
|
import registerForSetupHook from "./setup";
|
||||||
|
|
||||||
|
export default function registerForHooks(): void {
|
||||||
|
registerForHotbarDropHook();
|
||||||
|
registerForInitHook();
|
||||||
|
registerForReadyHook();
|
||||||
|
registerForRenderHooks();
|
||||||
|
registerForSetupHook();
|
||||||
|
}
|
28
src/module/hooks/hotbar-drop.ts
Normal file
28
src/module/hooks/hotbar-drop.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { DS4Item } from "../item/item";
|
||||||
|
import { DS4ItemData } from "../item/item-data";
|
||||||
|
import { createRollItemMacro } from "../macros/roll-item";
|
||||||
|
import notifications from "../ui/notifications";
|
||||||
|
|
||||||
|
export default function registerForHotbarDropHook(): void {
|
||||||
|
Hooks.on("hotbarDrop", async (hotbar: Hotbar, data: { type: string } & Record<string, unknown>, slot: string) => {
|
||||||
|
switch (data.type) {
|
||||||
|
case "Item": {
|
||||||
|
if (!("data" in data)) {
|
||||||
|
return notifications.warn(game.i18n.localize("DS4.WarningMacrosCanOnlyBeCreatedForOwnedItems"));
|
||||||
|
}
|
||||||
|
const itemData = data.data as DS4ItemData;
|
||||||
|
|
||||||
|
if (!DS4Item.rollableItemTypes.includes(itemData.type)) {
|
||||||
|
return notifications.warn(
|
||||||
|
game.i18n.format("DS4.WarningItemIsNotRollable", {
|
||||||
|
name: itemData.name,
|
||||||
|
id: itemData._id,
|
||||||
|
type: itemData.type,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await createRollItemMacro(itemData, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
58
src/module/hooks/init.ts
Normal file
58
src/module/hooks/init.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import { DS4Actor } from "../actor/actor";
|
||||||
|
import { DS4CharacterActorSheet } from "../actor/sheets/character-sheet";
|
||||||
|
import { DS4CreatureActorSheet } from "../actor/sheets/creature-sheet";
|
||||||
|
import { DS4 } from "../config";
|
||||||
|
import registerHandlebarsHelpers from "../handlebars/handlebars-helpers";
|
||||||
|
import registerHandlebarsPartials from "../handlebars/handlebars-partials";
|
||||||
|
import { DS4Item } from "../item/item";
|
||||||
|
import { DS4ItemSheet } from "../item/item-sheet";
|
||||||
|
import { macros } from "../macros/macros";
|
||||||
|
import { migration } from "../migrations";
|
||||||
|
import { DS4Check } from "../rolls/check";
|
||||||
|
import { createCheckRoll } from "../rolls/check-factory";
|
||||||
|
import { DS4Roll } from "../rolls/roll";
|
||||||
|
import registerSlayingDiceModifier from "../rolls/slaying-dice-modifier";
|
||||||
|
import { registerSystemSettings } from "../settings";
|
||||||
|
|
||||||
|
export default function registerForInitHook(): void {
|
||||||
|
Hooks.once("init", init);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
||||||
|
|
||||||
|
game.ds4 = {
|
||||||
|
DS4Actor,
|
||||||
|
DS4Item,
|
||||||
|
DS4,
|
||||||
|
createCheckRoll,
|
||||||
|
migration,
|
||||||
|
macros,
|
||||||
|
};
|
||||||
|
|
||||||
|
CONFIG.DS4 = DS4;
|
||||||
|
|
||||||
|
CONFIG.Actor.entityClass = DS4Actor;
|
||||||
|
CONFIG.Item.entityClass = DS4Item;
|
||||||
|
|
||||||
|
CONFIG.Actor.typeLabels = DS4.i18n.actorTypes;
|
||||||
|
CONFIG.Item.typeLabels = DS4.i18n.itemTypes;
|
||||||
|
|
||||||
|
CONFIG.Dice.types.push(DS4Check);
|
||||||
|
CONFIG.Dice.terms.s = DS4Check;
|
||||||
|
|
||||||
|
CONFIG.Dice.rolls.unshift(DS4Roll);
|
||||||
|
|
||||||
|
registerSlayingDiceModifier();
|
||||||
|
|
||||||
|
registerSystemSettings();
|
||||||
|
|
||||||
|
Actors.unregisterSheet("core", ActorSheet);
|
||||||
|
Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true });
|
||||||
|
Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true });
|
||||||
|
Items.unregisterSheet("core", ItemSheet);
|
||||||
|
Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
|
||||||
|
|
||||||
|
await registerHandlebarsPartials();
|
||||||
|
registerHandlebarsHelpers();
|
||||||
|
}
|
7
src/module/hooks/ready.ts
Normal file
7
src/module/hooks/ready.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { migration } from "../migrations";
|
||||||
|
|
||||||
|
export default function registerForReadyHook(): void {
|
||||||
|
Hooks.once("ready", () => {
|
||||||
|
migration.migrate();
|
||||||
|
});
|
||||||
|
}
|
23
src/module/hooks/render.ts
Normal file
23
src/module/hooks/render.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* @remarks The render hooks of all classes in the class hierarchy are called, so e.g. for a {@link Dialog}, both the
|
||||||
|
* "renderDialog" hook and the "renderApplication" hook are called (in this order).
|
||||||
|
*/
|
||||||
|
export default function registerForRenderHooks(): void {
|
||||||
|
["renderApplication", "renderActorSheet", "renderItemSheet"].forEach((hook) => {
|
||||||
|
Hooks.on(hook, selectTargetInputOnFocus);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the text of input elements in given application when focused via an on focus listener.
|
||||||
|
*
|
||||||
|
* @param app - The application in which to activate the listener.
|
||||||
|
* @param html - The {@link JQuery} representing the HTML of the application.
|
||||||
|
*/
|
||||||
|
function selectTargetInputOnFocus(app: Application, html: JQuery) {
|
||||||
|
$(html)
|
||||||
|
.find("input")
|
||||||
|
.on("focus", (ev: JQuery.FocusEvent<HTMLInputElement>) => {
|
||||||
|
ev.currentTarget.select();
|
||||||
|
});
|
||||||
|
}
|
28
src/module/hooks/setup.ts
Normal file
28
src/module/hooks/setup.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { DS4 } from "../config";
|
||||||
|
|
||||||
|
export default function registerForSetupHooks(): void {
|
||||||
|
Hooks.once("setup", () => {
|
||||||
|
localizeAndSortConfigObjects();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Localizes all objects in {@link DS4.i18n} and sorts them unless they are explicitly excluded.
|
||||||
|
*/
|
||||||
|
function localizeAndSortConfigObjects() {
|
||||||
|
const noSort = ["attributes", "traits", "combatValues", "creatureSizeCategories"];
|
||||||
|
|
||||||
|
const localizeObject = <T extends { [s: string]: string }>(obj: T, sort = true): T => {
|
||||||
|
const localized = Object.entries(obj).map(([key, value]) => {
|
||||||
|
return [key, game.i18n.localize(value)];
|
||||||
|
});
|
||||||
|
if (sort) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
||||||
|
return Object.fromEntries(localized);
|
||||||
|
};
|
||||||
|
|
||||||
|
DS4.i18n = Object.fromEntries(
|
||||||
|
Object.entries(DS4.i18n).map(([key, value]) => {
|
||||||
|
return [key, localizeObject(value, !noSort.includes(key))];
|
||||||
|
}),
|
||||||
|
) as typeof DS4.i18n;
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import { DS4Actor } from "../actor/actor";
|
||||||
import { DS4 } from "../config";
|
import { DS4 } from "../config";
|
||||||
import { createCheckRoll } from "../rolls/check-factory";
|
import { createCheckRoll } from "../rolls/check-factory";
|
||||||
import notifications from "../ui/notifications";
|
import notifications from "../ui/notifications";
|
||||||
import { AttackType, DS4ItemData } from "./item-data";
|
import { AttackType, DS4ItemData, ItemType } from "./item-data";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Item class for DS4
|
* The Item class for DS4
|
||||||
|
@ -40,6 +40,13 @@ export class DS4Item extends Item<DS4ItemData> {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of item types that are rollable.
|
||||||
|
*/
|
||||||
|
static get rollableItemTypes(): ItemType[] {
|
||||||
|
return ["weapon", "spell"];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Roll a check for a action with this item.
|
* Roll a check for a action with this item.
|
||||||
*/
|
*/
|
||||||
|
@ -71,8 +78,8 @@ export class DS4Item extends Item<DS4ItemData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.data.data.equipped) {
|
if (!this.data.data.equipped) {
|
||||||
throw new Error(
|
return notifications.warn(
|
||||||
game.i18n.format("DS4.ErrorItemMustBeEquippedToBeRolled", {
|
game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.data.type,
|
type: this.data.type,
|
||||||
|
@ -104,8 +111,8 @@ export class DS4Item extends Item<DS4ItemData> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.data.data.equipped) {
|
if (!this.data.data.equipped) {
|
||||||
throw new Error(
|
return notifications.warn(
|
||||||
game.i18n.format("DS4.ErrorItemMustBeEquippedToBeRolled", {
|
game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
id: this.id,
|
id: this.id,
|
||||||
type: this.data.type,
|
type: this.data.type,
|
||||||
|
@ -158,7 +165,6 @@ export class DS4Item extends Item<DS4ItemData> {
|
||||||
}
|
}
|
||||||
return `${selectedAttackType}Attack` as const;
|
return `${selectedAttackType}Attack` as const;
|
||||||
},
|
},
|
||||||
render: () => undefined, // TODO(types): This is actually optional, remove when types are updated )
|
|
||||||
options: { jQuery: true },
|
options: { jQuery: true },
|
||||||
});
|
});
|
||||||
return answer;
|
return answer;
|
||||||
|
|
5
src/module/macros/macros.ts
Normal file
5
src/module/macros/macros.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { rollItem } from "./roll-item";
|
||||||
|
|
||||||
|
export const macros = {
|
||||||
|
rollItem: rollItem,
|
||||||
|
};
|
54
src/module/macros/roll-item.ts
Normal file
54
src/module/macros/roll-item.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import { DS4Actor } from "../actor/actor";
|
||||||
|
import { getCanvas } from "../helpers";
|
||||||
|
import { DS4ItemData } from "../item/item-data";
|
||||||
|
import notifications from "../ui/notifications";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a macro from an item drop.
|
||||||
|
* Get an existing roll item macro if one exists, otherwise create a new one.
|
||||||
|
* @param itemData - The item data
|
||||||
|
* @param slot - The hotbar slot to use
|
||||||
|
*/
|
||||||
|
export async function createRollItemMacro(itemData: DS4ItemData, slot: string): Promise<void> {
|
||||||
|
const command = `game.ds4.macros.rollItem("${itemData._id}");`;
|
||||||
|
const macro =
|
||||||
|
game.macros?.entities.find((m) => m.name === itemData.name && m.data.command === command) ??
|
||||||
|
(await Macro.create(
|
||||||
|
{
|
||||||
|
command,
|
||||||
|
name: itemData.name,
|
||||||
|
type: "script",
|
||||||
|
img: itemData.img,
|
||||||
|
flags: { "ds4.itemMacro": true },
|
||||||
|
},
|
||||||
|
{ displaySheet: false },
|
||||||
|
));
|
||||||
|
game.user?.assignHotbarMacro(macro, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the roll item macro for the given itemId.
|
||||||
|
* @param itemId
|
||||||
|
*/
|
||||||
|
export async function rollItem(itemId: string): Promise<void> {
|
||||||
|
const speaker = ChatMessage.getSpeaker();
|
||||||
|
const actor = (getCanvas().tokens.get(speaker.token ?? "")?.actor ?? game.actors?.get(speaker.actor ?? "")) as
|
||||||
|
| DS4Actor
|
||||||
|
| null
|
||||||
|
| undefined;
|
||||||
|
if (!actor) {
|
||||||
|
return notifications.warn(game.i18n.localize("DS4.WarningMustControlActorToUseRollItemMacro"));
|
||||||
|
}
|
||||||
|
const item = actor.items?.get(itemId);
|
||||||
|
if (!item) {
|
||||||
|
return notifications.warn(
|
||||||
|
game.i18n.format("DS4.WarningControlledActorDoesNotHaveItem", {
|
||||||
|
actorName: actor.name,
|
||||||
|
actorId: actor.id,
|
||||||
|
itemId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.roll();
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ export class DS4Roll<D extends Record<string, unknown> = Record<string, unknown>
|
||||||
* template if the first dice term is a ds4 check.
|
* template if the first dice term is a ds4 check.
|
||||||
* @override
|
* @override
|
||||||
*/
|
*/
|
||||||
async render(chatOptions: Roll.ChatOptions = {}): Promise<HTMLElement> {
|
async render(chatOptions: Roll.ChatOptions = {}): Promise<string> {
|
||||||
chatOptions = mergeObject(
|
chatOptions = mergeObject(
|
||||||
{
|
{
|
||||||
user: game.user?._id,
|
user: game.user?._id,
|
||||||
|
@ -39,6 +39,6 @@ export class DS4Roll<D extends Record<string, unknown> = Record<string, unknown>
|
||||||
};
|
};
|
||||||
|
|
||||||
// Render the roll display template
|
// Render the roll display template
|
||||||
return (renderTemplate(chatOptions.template ?? "", chatData) as unknown) as Promise<HTMLElement>; // TODO(types): Make this cast unnecessary by fixing upstream
|
return renderTemplate(chatOptions.template ?? "", chatData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import { DS4Check } from "./check";
|
import { DS4Check } from "./check";
|
||||||
|
|
||||||
export default function registerSlayingDiceModifier(): void {
|
export default function registerSlayingDiceModifier(): void {
|
||||||
// TODO(types): Adjust types to allow extension of DiceTerm.MODIFIERS (see https://github.com/League-of-Foundry-Developers/foundry-vtt-types/pull/573)
|
|
||||||
// eslint-disable-next-line
|
|
||||||
// @ts-ignore
|
|
||||||
DicePool.MODIFIERS.x = slay;
|
DicePool.MODIFIERS.x = slay;
|
||||||
DicePool.POOL_REGEX = /^{([^}]+)}([A-z]([A-z0-9<=>]+)?)?$/;
|
DicePool.POOL_REGEX = /^{([^}]+)}([A-z]([A-z0-9<=>]+)?)?$/;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
@use "../utils/colors";
|
@use "../utils/colors";
|
||||||
|
|
||||||
.ds4-dice-total {
|
// Needs to be nested in .dice-roll to win against foundry's style.css with respect to specificity
|
||||||
|
.dice-roll .ds4-dice-total {
|
||||||
@mixin color-filter($rotation) {
|
@mixin color-filter($rotation) {
|
||||||
filter: sepia(0.5) hue-rotate($rotation);
|
filter: sepia(0.5) hue-rotate($rotation);
|
||||||
backdrop-filter: sepia(0) hue-rotate($rotation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&--coup {
|
&--coup {
|
||||||
color: colors.$c-coup;
|
|
||||||
@include color-filter(60deg);
|
@include color-filter(60deg);
|
||||||
|
background-color: colors.$c-coup-bg;
|
||||||
|
color: colors.$c-coup;
|
||||||
}
|
}
|
||||||
|
|
||||||
&--fumble {
|
&--fumble {
|
||||||
color: colors.$c-fumble;
|
|
||||||
@include color-filter(-60deg);
|
@include color-filter(-60deg);
|
||||||
|
background-color: colors.$c-fumble-bg;
|
||||||
|
color: colors.$c-fumble;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,6 @@ $c-light-grey: #777;
|
||||||
$c-border-groove: #eeede0;
|
$c-border-groove: #eeede0;
|
||||||
$c-invalid-input: rgba(lightcoral, 50%);
|
$c-invalid-input: rgba(lightcoral, 50%);
|
||||||
$c-coup: #18520b;
|
$c-coup: #18520b;
|
||||||
|
$c-coup-bg: #acc2a7;
|
||||||
$c-fumble: #aa0200;
|
$c-fumble: #aa0200;
|
||||||
|
$c-fumble-bg: #d8b5ba;
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -647,9 +647,9 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@league-of-foundry-developers/foundry-vtt-types@npm:^0.7.9-1":
|
"@league-of-foundry-developers/foundry-vtt-types@npm:^0.7.9-3":
|
||||||
version: 0.7.9-1
|
version: 0.7.9-3
|
||||||
resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.7.9-1"
|
resolution: "@league-of-foundry-developers/foundry-vtt-types@npm:0.7.9-3"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/howler": 2.2.1
|
"@types/howler": 2.2.1
|
||||||
"@types/jquery": 3.5.1
|
"@types/jquery": 3.5.1
|
||||||
|
@ -659,7 +659,7 @@ __metadata:
|
||||||
pixi.js: 5.3.4
|
pixi.js: 5.3.4
|
||||||
tinymce: 5.6.2
|
tinymce: 5.6.2
|
||||||
typescript: ^4.1.4
|
typescript: ^4.1.4
|
||||||
checksum: 1b2e311e5ab2db9da4d67fee7b5ab038b228b76ff78f90830f770ae4cee7191f652a18dec0161cc7e2e9696fc06969e23b33a0402249bc4251cc557fa5efc430
|
checksum: 75524c7aa78ddb77cad1a9d041af30ae5bbd708f5b26568dabbb3d913a4643aefcc6f2ed80e1e76b3c17050579665eab155f035f840db6397691cf68eeee9b3f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -2820,7 +2820,7 @@ __metadata:
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "dungeonslayers4@workspace:."
|
resolution: "dungeonslayers4@workspace:."
|
||||||
dependencies:
|
dependencies:
|
||||||
"@league-of-foundry-developers/foundry-vtt-types": ^0.7.9-1
|
"@league-of-foundry-developers/foundry-vtt-types": ^0.7.9-3
|
||||||
"@types/fs-extra": ^9.0.8
|
"@types/fs-extra": ^9.0.8
|
||||||
"@types/jest": ^26.0.20
|
"@types/jest": ^26.0.20
|
||||||
"@typescript-eslint/eslint-plugin": ^4.16.1
|
"@typescript-eslint/eslint-plugin": ^4.16.1
|
||||||
|
|
Loading…
Reference in a new issue