chore: reformat with 2 spaces and single quotes
Some checks failed
ci/woodpecker/pr/checks Pipeline failed

This commit is contained in:
Johannes Loher 2023-07-10 22:10:48 +02:00
parent cb0678f330
commit 3a3a37d2c8
Signed by: saluu
GPG key ID: 7CB0A9FB553DA045
1589 changed files with 70375 additions and 70407 deletions

View file

@ -3,13 +3,13 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
module.exports = { module.exports = {
parser: "@typescript-eslint/parser", parser: '@typescript-eslint/parser',
parserOptions: { ecmaVersion: 2020, sourceType: "module" }, parserOptions: { ecmaVersion: 2020, sourceType: 'module' },
env: { browser: true }, env: { browser: true },
extends: ["plugin:@typescript-eslint/recommended", "prettier"], extends: ['plugin:@typescript-eslint/recommended', 'prettier'],
plugins: ["@typescript-eslint"], plugins: ['@typescript-eslint'],
overrides: [ overrides: [
{ files: ["./*.cjs"], rules: { "@typescript-eslint/no-var-requires": "off" } }, { files: ['./*.cjs'], rules: { '@typescript-eslint/no-var-requires': 'off' } },
{ files: ["./spec/**/*"], env: { browser: false } }, { files: ['./spec/**/*'], env: { browser: false } },
], ],
}; };

View file

@ -4,8 +4,8 @@
export default { export default {
semi: true, semi: true,
trailingComma: "all", trailingComma: 'all',
singleQuote: false, singleQuote: true,
printWidth: 120, printWidth: 120,
tabWidth: 4, tabWidth: 2,
}; };

View file

@ -2,4 +2,4 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
module.exports = { extends: ["@commitlint/config-conventional"] }; module.exports = { extends: ['@commitlint/config-conventional'] };

View file

@ -2,29 +2,29 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import styles from "@ironkinoko/rollup-plugin-styles"; import styles from '@ironkinoko/rollup-plugin-styles';
import { swc } from "rollup-plugin-swc3"; import { swc } from 'rollup-plugin-swc3';
import { copy } from "@guanghechen/rollup-plugin-copy"; import { copy } from '@guanghechen/rollup-plugin-copy';
import { distDirectory, name, sourceDirectory } from "./tools/const.js"; import { distDirectory, name, sourceDirectory } from './tools/const.js';
const staticFiles = [ const staticFiles = [
".reuse", '.reuse',
"assets", 'assets',
"ATTRIBUTION.md", 'ATTRIBUTION.md',
"fonts", 'fonts',
"lang", 'lang',
"LICENSE.md", 'LICENSE.md',
"LICENSES", 'LICENSES',
"README.md", 'README.md',
"system.json.license", 'system.json.license',
"system.json", 'system.json',
"template.json.license", 'template.json.license',
"template.json", 'template.json',
"templates", 'templates',
]; ];
const isProduction = process.env.NODE_ENV === "production"; const isProduction = process.env.NODE_ENV === 'production';
/** /**
* @type {import('rollup').RollupOptions} * @type {import('rollup').RollupOptions}
@ -33,9 +33,9 @@ const config = {
input: { [name]: `${sourceDirectory}/${name}.ts` }, input: { [name]: `${sourceDirectory}/${name}.ts` },
output: { output: {
dir: distDirectory, dir: distDirectory,
format: "es", format: 'es',
sourcemap: true, sourcemap: true,
assetFileNames: "[name].[ext]", assetFileNames: '[name].[ext]',
}, },
plugins: [ plugins: [
swc({ swc({
@ -51,7 +51,7 @@ const config = {
sourceMaps: true, sourceMaps: true,
}), }),
styles({ styles({
mode: ["extract", `css/${name}.css`], mode: ['extract', `css/${name}.css`],
url: false, url: false,
sourceMap: true, sourceMap: true,
minimize: isProduction, minimize: isProduction,

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/mixins"; @use '../../utils/mixins';
.ds4-actor-header { .ds4-actor-header {
display: flex; display: flex;
@ -44,7 +44,7 @@
margin: 0; margin: 0;
} }
&__name-input[type="text"] { &__name-input[type='text'] {
@include mixins.font-heading-upper; @include mixins.font-heading-upper;
background-color: transparent; background-color: transparent;
border: none; border: none;

View file

@ -5,9 +5,9 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/colors"; @use '../../utils/colors';
@use "../../utils/mixins"; @use '../../utils/mixins';
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-actor-progression { .ds4-actor-progression {
@include mixins.mark-invalid-or-disabled-input; @include mixins.mark-invalid-or-disabled-input;

View file

@ -6,8 +6,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/mixins"; @use '../../utils/mixins';
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-actor-properties { .ds4-actor-properties {
@include mixins.mark-invalid-or-disabled-input; @include mixins.mark-invalid-or-disabled-input;

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/mixins"; @use '../../utils/mixins';
.ds4-check { .ds4-check {
background: transparent; background: transparent;

View file

@ -4,8 +4,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/mixins"; @use '../../utils/mixins';
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-combat-value { .ds4-combat-value {
$size: 3.75rem; $size: 3.75rem;
@ -15,7 +15,7 @@
row-gap: 0.125em; row-gap: 0.125em;
&__value { &__value {
$combat-values-icons-path: "#{variables.$official-icons-path}/combat-values"; $combat-values-icons-path: '#{variables.$official-icons-path}/combat-values';
@include mixins.centered-content; @include mixins.centered-content;
background-position: center; background-position: center;
@ -26,28 +26,28 @@
width: $size; width: $size;
&--hitPoints { &--hitPoints {
background-image: url("#{$combat-values-icons-path}/hit-points.png"); background-image: url('#{$combat-values-icons-path}/hit-points.png');
} }
&--defense { &--defense {
background-image: url("#{$combat-values-icons-path}/defense.png"); background-image: url('#{$combat-values-icons-path}/defense.png');
} }
&--initiative { &--initiative {
background-image: url("#{$combat-values-icons-path}/initiative.png"); background-image: url('#{$combat-values-icons-path}/initiative.png');
} }
&--movement { &--movement {
background-image: url("#{$combat-values-icons-path}/movement-rate.png"); background-image: url('#{$combat-values-icons-path}/movement-rate.png');
} }
&--meleeAttack { &--meleeAttack {
background-image: url("#{$combat-values-icons-path}/melee-attack.png"); background-image: url('#{$combat-values-icons-path}/melee-attack.png');
} }
&--rangedAttack { &--rangedAttack {
background-image: url("#{$combat-values-icons-path}/ranged-attack.png"); background-image: url('#{$combat-values-icons-path}/ranged-attack.png');
} }
&--spellcasting { &--spellcasting {
background-image: url("#{$combat-values-icons-path}/spellcasting.png"); background-image: url('#{$combat-values-icons-path}/spellcasting.png');
} }
&--targetedSpellcasting { &--targetedSpellcasting {
background-image: url("#{$combat-values-icons-path}/targeted-spellcasting.png"); background-image: url('#{$combat-values-icons-path}/targeted-spellcasting.png');
} }
} }

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-combat-values { .ds4-combat-values {
border-bottom: variables.$border-groove; border-bottom: variables.$border-groove;

View file

@ -4,9 +4,9 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/colors"; @use '../../utils/colors';
@use "../../utils/mixins"; @use '../../utils/mixins';
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-core-value { .ds4-core-value {
align-items: center; align-items: center;

View file

@ -4,8 +4,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/colors"; @use '../../utils/colors';
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-core-values { .ds4-core-values {
column-gap: 0.5em; column-gap: 0.5em;

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-currency { .ds4-currency {
align-items: center; align-items: center;

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/colors"; @use '../../utils/colors';
// Needs to be nested in .dice-roll to win against foundry's style.css with respect to specificity // Needs to be nested in .dice-roll to win against foundry's style.css with respect to specificity
.dice-roll .ds4-dice-total { .dice-roll .ds4-dice-total {

View file

@ -4,8 +4,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/colors"; @use '../../utils/colors';
@use "../../utils/mixins"; @use '../../utils/mixins';
.ds4-item-header { .ds4-item-header {
align-items: center; align-items: center;
@ -39,7 +39,7 @@
display: none; display: none;
} }
&__name-input[type="text"] { &__name-input[type='text'] {
@include mixins.font-heading-upper; @include mixins.font-heading-upper;
background-color: transparent; background-color: transparent;
border: none; border: none;

View file

@ -5,8 +5,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/mixins"; @use '../../utils/mixins';
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-item-properties { .ds4-item-properties {
@include mixins.mark-invalid-or-disabled-input; @include mixins.mark-invalid-or-disabled-input;

View file

@ -21,7 +21,7 @@
} }
} }
&__checkbox[type="checkbox"] { &__checkbox[type='checkbox'] {
margin: 0; margin: 0;
} }
} }

View file

@ -5,8 +5,8 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/mixins"; @use '../../utils/mixins';
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-embedded-document-list { .ds4-embedded-document-list {
@include mixins.mark-invalid-or-disabled-input; @include mixins.mark-invalid-or-disabled-input;
@ -105,8 +105,8 @@
} }
&__editable { &__editable {
&[type="text"], &[type='text'],
&[type="number"] { &[type='number'] {
background-color: transparent; background-color: transparent;
border: 0; border: 0;
padding: 0; padding: 0;
@ -114,7 +114,7 @@
} }
&--checkbox { &--checkbox {
&[type="checkbox"] { &[type='checkbox'] {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0px; margin: 0px;

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "../../utils/variables"; @use '../../utils/variables';
.ds4-sheet-tab-nav { .ds4-sheet-tab-nav {
border-bottom: variables.$border-groove; border-bottom: variables.$border-groove;

View file

@ -7,43 +7,43 @@
*/ */
// global // global
@use "global/accessibility"; @use 'global/accessibility';
@use "global/fonts"; @use 'global/fonts';
@use "global/utils"; @use 'global/utils';
// shared // shared
@use "components/shared/add_button"; @use 'components/shared/add_button';
@use "components/shared/control_button_group"; @use 'components/shared/control_button_group';
@use "components/shared/checkbox_grid"; @use 'components/shared/checkbox_grid';
@use "components/shared/editor"; @use 'components/shared/editor';
@use "components/shared/embedded_document_list"; @use 'components/shared/embedded_document_list';
@use "components/shared/form_group"; @use 'components/shared/form_group';
@use "components/shared/rollable_image"; @use 'components/shared/rollable_image';
@use "components/shared/sheet_body"; @use 'components/shared/sheet_body';
@use "components/shared/sheet_form"; @use 'components/shared/sheet_form';
@use "components/shared/sheet_tab_nav"; @use 'components/shared/sheet_tab_nav';
@use "components/shared/sheet_tab"; @use 'components/shared/sheet_tab';
// actor // actor
@use "components/actor/actor_header"; @use 'components/actor/actor_header';
@use "components/actor/actor_progression"; @use 'components/actor/actor_progression';
@use "components/actor/actor_properties"; @use 'components/actor/actor_properties';
@use "components/actor/actor_sheet"; @use 'components/actor/actor_sheet';
@use "components/actor/biography"; @use 'components/actor/biography';
@use "components/actor/check"; @use 'components/actor/check';
@use "components/actor/checks"; @use 'components/actor/checks';
@use "components/actor/combat_value"; @use 'components/actor/combat_value';
@use "components/actor/combat_values"; @use 'components/actor/combat_values';
@use "components/actor/core_value"; @use 'components/actor/core_value';
@use "components/actor/core_values"; @use 'components/actor/core_values';
@use "components/actor/currency"; @use 'components/actor/currency';
@use "components/actor/description"; @use 'components/actor/description';
@use "components/actor/profile"; @use 'components/actor/profile';
// item // item
@use "components/item/item_header"; @use 'components/item/item_header';
@use "components/item/item_properties"; @use 'components/item/item_properties';
@use "components/item/item_sheet"; @use 'components/item/item_sheet';
// dice // dice
@use "components/dice/dice_total"; @use 'components/dice/dice_total';

View file

@ -6,55 +6,55 @@
@font-face { @font-face {
font-display: swap; font-display: swap;
font-family: "Lora"; font-family: 'Lora';
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
src: src:
local("Lora"), local('Lora'),
url("../fonts/Lora/Lora.woff") format("woff"); url('../fonts/Lora/Lora.woff') format('woff');
} }
@font-face { @font-face {
font-display: swap; font-display: swap;
font-family: "Lora"; font-family: 'Lora';
font-style: normal; font-style: normal;
font-weight: bold; font-weight: bold;
src: src:
local("Lora"), local('Lora'),
url("../fonts/Lora/Lora-Bold.woff") format("woff"); url('../fonts/Lora/Lora-Bold.woff') format('woff');
} }
@font-face { @font-face {
font-display: swap; font-display: swap;
font-family: "Lora"; font-family: 'Lora';
font-style: italic; font-style: italic;
font-weight: normal; font-weight: normal;
src: src:
local("Lora"), local('Lora'),
url("../fonts/Lora/Lora-Italic.woff") format("woff"); url('../fonts/Lora/Lora-Italic.woff') format('woff');
} }
@font-face { @font-face {
font-display: swap; font-display: swap;
font-family: "Lora"; font-family: 'Lora';
font-style: italic; font-style: italic;
font-weight: bold; font-weight: bold;
src: src:
local("Lora"), local('Lora'),
url("../fonts/Lora/Lora-BoldItalic.woff") format("woff"); url('../fonts/Lora/Lora-BoldItalic.woff') format('woff');
} }
@font-face { @font-face {
font-display: swap; font-display: swap;
font-family: "Wood Stamp"; font-family: 'Wood Stamp';
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
src: src:
local("Wood Stamp"), local('Wood Stamp'),
url("../fonts/Woodstamp/Woodstamp.woff") format("woff"); url('../fonts/Woodstamp/Woodstamp.woff') format('woff');
} }
:root { :root {
--ds4-font-primary: Lora, serif; --ds4-font-primary: Lora, serif;
--ds4-font-heading: "Wood Stamp", sans-serif; --ds4-font-heading: 'Wood Stamp', sans-serif;
} }

View file

@ -5,7 +5,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "./colors"; @use './colors';
@mixin centered-content { @mixin centered-content {
display: grid; display: grid;

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@use "./colors"; @use './colors';
$padding-sm: 5px; $padding-sm: 5px;
$padding-md: 10px; $padding-md: 10px;
@ -13,6 +13,6 @@ $margin-sm: $padding-sm;
$margin-md: $padding-md; $margin-md: $padding-md;
$margin-lg: $padding-lg; $margin-lg: $padding-lg;
$official-icons-path: "../assets/icons/official"; $official-icons-path: '../assets/icons/official';
$border-groove: 2px groove colors.$c-border-groove; $border-groove: 2px groove colors.$c-border-groove;

View file

@ -3,38 +3,38 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { describe, expect, it } from "vitest"; import { describe, expect, it } from 'vitest';
import { evaluateCheck } from "../../src/dice/check-evaluation"; import { evaluateCheck } from '../../src/dice/check-evaluation';
describe("evaluateCheck with no dice", () => { describe('evaluateCheck with no dice', () => {
it("should throw an error.", () => { it('should throw an error.', () => {
expect(() => evaluateCheck([], 10)).toThrow("Invalid number of dice."); expect(() => evaluateCheck([], 10)).toThrow('Invalid number of dice.');
}); });
}); });
describe("evaluateCheck with more dice than required by the checkTargetNumber", () => { describe('evaluateCheck with more dice than required by the checkTargetNumber', () => {
it("should throw an error.", () => { it('should throw an error.', () => {
expect(() => evaluateCheck([10, 10], 10)).toThrow("Invalid number of dice."); expect(() => evaluateCheck([10, 10], 10)).toThrow('Invalid number of dice.');
}); });
}); });
describe("evaluateCheck with less dice than required by the checkTargetNumber", () => { describe('evaluateCheck with less dice than required by the checkTargetNumber', () => {
it("should throw an error.", () => { it('should throw an error.', () => {
expect(() => evaluateCheck([10], 21)).toThrow("Invalid number of dice."); expect(() => evaluateCheck([10], 21)).toThrow('Invalid number of dice.');
}); });
}); });
describe("evaluateCheck with a single die", () => { describe('evaluateCheck with a single die', () => {
it("should assign the checkTargetNumber to the single die and be successful.", () => { it('should assign the checkTargetNumber to the single die and be successful.', () => {
expect(evaluateCheck([4], 12)).toEqual([{ result: 4, checkTargetNumber: 12, active: true, discarded: false }]); expect(evaluateCheck([4], 12)).toEqual([{ result: 4, checkTargetNumber: 12, active: true, discarded: false }]);
}); });
it("should assign the checkTargetNumber to the single die on upper edge case and be successful.", () => { it('should assign the checkTargetNumber to the single die on upper edge case and be successful.', () => {
expect(evaluateCheck([4], 4)).toEqual([{ result: 4, checkTargetNumber: 4, active: true, discarded: false }]); expect(evaluateCheck([4], 4)).toEqual([{ result: 4, checkTargetNumber: 4, active: true, discarded: false }]);
}); });
it("should assign the checkTargetNumber to the single die on lower edge case not be successful.", () => { it('should assign the checkTargetNumber to the single die on lower edge case not be successful.', () => {
expect(evaluateCheck([5], 4)).toEqual([{ result: 5, checkTargetNumber: 4, active: false, discarded: true }]); expect(evaluateCheck([5], 4)).toEqual([{ result: 5, checkTargetNumber: 4, active: false, discarded: true }]);
}); });
@ -54,48 +54,44 @@ describe("evaluateCheck with a single die", () => {
]); ]);
}); });
it("should roll a die even when the checkTargetNumber is 0 and coup on 1", () => { it('should roll a die even when the checkTargetNumber is 0 and coup on 1', () => {
expect(evaluateCheck([1], 0)).toEqual([ expect(evaluateCheck([1], 0)).toEqual([
{ result: 1, checkTargetNumber: 0, active: true, discarded: false, success: true, count: 0 }, { result: 1, checkTargetNumber: 0, active: true, discarded: false, success: true, count: 0 },
]); ]);
}); });
it("should roll a die even when the checkTargetNumber is 0 and fail on values > 1 and < 20", () => { it('should roll a die even when the checkTargetNumber is 0 and fail on values > 1 and < 20', () => {
for (let i = 2; i < 20; i++) { for (let i = 2; i < 20; i++) {
expect(evaluateCheck([i], 0)).toEqual([ expect(evaluateCheck([i], 0)).toEqual([{ result: i, checkTargetNumber: 0, active: false, discarded: true }]);
{ result: i, checkTargetNumber: 0, active: false, discarded: true },
]);
} }
}); });
it("should roll a die even when the checkTargetNumber is 0 and fumble on 20", () => { it('should roll a die even when the checkTargetNumber is 0 and fumble on 20', () => {
expect(evaluateCheck([20], 0)).toEqual([ expect(evaluateCheck([20], 0)).toEqual([
{ result: 20, checkTargetNumber: 0, active: false, discarded: true, failure: true }, { result: 20, checkTargetNumber: 0, active: false, discarded: true, failure: true },
]); ]);
}); });
it("should roll a die even when the checkTargetNumber is < 0 and coup on 1", () => { it('should roll a die even when the checkTargetNumber is < 0 and coup on 1', () => {
expect(evaluateCheck([1], -1)).toEqual([ expect(evaluateCheck([1], -1)).toEqual([
{ result: 1, checkTargetNumber: -1, active: true, discarded: false, success: true, count: -1 }, { result: 1, checkTargetNumber: -1, active: true, discarded: false, success: true, count: -1 },
]); ]);
}); });
it("should roll a die even when the checkTargetNumber is < 0 and fail on values > 1 and < 20", () => { it('should roll a die even when the checkTargetNumber is < 0 and fail on values > 1 and < 20', () => {
for (let i = 2; i < 20; i++) { for (let i = 2; i < 20; i++) {
expect(evaluateCheck([i], -1)).toEqual([ expect(evaluateCheck([i], -1)).toEqual([{ result: i, checkTargetNumber: -1, active: false, discarded: true }]);
{ result: i, checkTargetNumber: -1, active: false, discarded: true },
]);
} }
}); });
it("should roll a die even when the checkTargetNumber is < 0 and fumble on 20", () => { it('should roll a die even when the checkTargetNumber is < 0 and fumble on 20', () => {
expect(evaluateCheck([20], -1)).toEqual([ expect(evaluateCheck([20], -1)).toEqual([
{ result: 20, checkTargetNumber: -1, active: false, discarded: true, failure: true }, { result: 20, checkTargetNumber: -1, active: false, discarded: true, failure: true },
]); ]);
}); });
}); });
describe("evaluateCheck with a single die and coup / fumble modification", () => { describe('evaluateCheck with a single die and coup / fumble modification', () => {
const maximumCoupResult = 2; const maximumCoupResult = 2;
const minimumFumbleResult = 19; const minimumFumbleResult = 19;
@ -185,7 +181,7 @@ describe("evaluateCheck with a single die and coup / fumble modification", () =>
}); });
}); });
describe("evaluateCheck with multiple dice", () => { describe('evaluateCheck with multiple dice', () => {
it("should assign the checkTargetNumber for the last sub check to the lowest non coup, even if the first is '20'.", () => { it("should assign the checkTargetNumber for the last sub check to the lowest non coup, even if the first is '20'.", () => {
expect(evaluateCheck([20, 6, 15], 48)).toEqual([ expect(evaluateCheck([20, 6, 15], 48)).toEqual([
{ result: 20, checkTargetNumber: 20, active: true, discarded: false, failure: true }, { result: 20, checkTargetNumber: 20, active: true, discarded: false, failure: true },
@ -194,7 +190,7 @@ describe("evaluateCheck with multiple dice", () => {
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to the first coup if there are only coups.", () => { it('should assign the checkTargetNumber for the last sub check to the first coup if there are only coups.', () => {
expect(evaluateCheck([1, 1, 1], 48)).toEqual([ expect(evaluateCheck([1, 1, 1], 48)).toEqual([
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 }, { result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 }, { result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
@ -202,7 +198,7 @@ describe("evaluateCheck with multiple dice", () => {
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.", () => { it('should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.', () => {
expect(evaluateCheck([15, 15, 15], 48)).toEqual([ expect(evaluateCheck([15, 15, 15], 48)).toEqual([
{ result: 15, checkTargetNumber: 8, active: false, discarded: true }, { result: 15, checkTargetNumber: 8, active: false, discarded: true },
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
@ -210,7 +206,7 @@ describe("evaluateCheck with multiple dice", () => {
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to the first coup if its sum with the lowest non coup is high enough.", () => { it('should assign the checkTargetNumber for the last sub check to the first coup if its sum with the lowest non coup is high enough.', () => {
expect(evaluateCheck([15, 15, 1], 48)).toEqual([ expect(evaluateCheck([15, 15, 1], 48)).toEqual([
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
@ -226,7 +222,7 @@ describe("evaluateCheck with multiple dice", () => {
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.", () => { it('should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.', () => {
expect(evaluateCheck([15, 4, 12], 46)).toEqual([ expect(evaluateCheck([15, 4, 12], 46)).toEqual([
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
{ result: 4, checkTargetNumber: 6, active: true, discarded: false }, { result: 4, checkTargetNumber: 6, active: true, discarded: false },
@ -234,7 +230,7 @@ describe("evaluateCheck with multiple dice", () => {
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.", () => { it('should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.', () => {
expect(evaluateCheck([15, 8, 12], 46)).toEqual([ expect(evaluateCheck([15, 8, 12], 46)).toEqual([
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
{ result: 8, checkTargetNumber: 6, active: false, discarded: true }, { result: 8, checkTargetNumber: 6, active: false, discarded: true },
@ -264,7 +260,7 @@ describe("evaluateCheck with multiple dice", () => {
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when there is more than one coup and a coup is used for the last sub CTN", () => { it('should assign the checkTargetNumber for the last sub check to properly maximize the result when there is more than one coup and a coup is used for the last sub CTN', () => {
expect(evaluateCheck([1, 1, 15], 48)).toEqual([ expect(evaluateCheck([1, 1, 15], 48)).toEqual([
{ result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 }, { result: 1, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
{ result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 }, { result: 1, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },
@ -273,7 +269,7 @@ describe("evaluateCheck with multiple dice", () => {
}); });
}); });
describe("evaluateCheck with multiple dice and coup / fumble modification", () => { describe('evaluateCheck with multiple dice and coup / fumble modification', () => {
it("should assign the checkTargetNumber for the last sub check to the lowest non coup and fumble if the first is '19'.", () => { it("should assign the checkTargetNumber for the last sub check to the lowest non coup and fumble if the first is '19'.", () => {
expect(evaluateCheck([19, 15, 6], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([ expect(evaluateCheck([19, 15, 6], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
{ result: 19, checkTargetNumber: 20, active: true, discarded: false, failure: true }, { result: 19, checkTargetNumber: 20, active: true, discarded: false, failure: true },
@ -290,7 +286,7 @@ describe("evaluateCheck with multiple dice and coup / fumble modification", () =
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.", () => { it('should assign the checkTargetNumber for the last sub check to the first lowest die, even if it is higher than that value.', () => {
expect(evaluateCheck([15, 15, 15], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([ expect(evaluateCheck([15, 15, 15], 48, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
{ result: 15, checkTargetNumber: 8, active: false, discarded: true }, { result: 15, checkTargetNumber: 8, active: false, discarded: true },
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
@ -322,7 +318,7 @@ describe("evaluateCheck with multiple dice and coup / fumble modification", () =
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.", () => { it('should assign the checkTargetNumber for the last sub check to properly maximize the result when all dice are successes.', () => {
expect(evaluateCheck([15, 4, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([ expect(evaluateCheck([15, 4, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
{ result: 4, checkTargetNumber: 6, active: true, discarded: false }, { result: 4, checkTargetNumber: 6, active: true, discarded: false },
@ -330,7 +326,7 @@ describe("evaluateCheck with multiple dice and coup / fumble modification", () =
]); ]);
}); });
it("should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.", () => { it('should assign the checkTargetNumber for the last sub check to properly maximize the result when one dice is a failure.', () => {
expect(evaluateCheck([15, 8, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([ expect(evaluateCheck([15, 8, 12], 46, { maximumCoupResult: 2, minimumFumbleResult: 19 })).toEqual([
{ result: 15, checkTargetNumber: 20, active: true, discarded: false }, { result: 15, checkTargetNumber: 20, active: true, discarded: false },
{ result: 8, checkTargetNumber: 6, active: false, discarded: true }, { result: 8, checkTargetNumber: 6, active: false, discarded: true },
@ -368,7 +364,7 @@ describe("evaluateCheck with multiple dice and coup / fumble modification", () =
]); ]);
}); });
it("should use all the dice if they are coups, even if they are higher than the checkTargetNumber", () => { it('should use all the dice if they are coups, even if they are higher than the checkTargetNumber', () => {
expect(evaluateCheck([18, 19, 17], 48, { maximumCoupResult: 19 })).toEqual([ expect(evaluateCheck([18, 19, 17], 48, { maximumCoupResult: 19 })).toEqual([
{ result: 18, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 }, { result: 18, checkTargetNumber: 8, active: true, discarded: false, success: true, count: 8 },
{ result: 19, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 }, { result: 19, checkTargetNumber: 20, active: true, discarded: false, success: true, count: 20 },

View file

@ -2,19 +2,19 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { describe, expect, it } from "vitest"; import { describe, expect, it } from 'vitest';
import { calculateSpellPrice } from "../../../../src/documents/item/spell/calculate-spell-price"; import { calculateSpellPrice } from '../../../../src/documents/item/spell/calculate-spell-price';
import type { CooldownDuration, DS4SpellDataSourceData } from "../../../../src/documents/item/spell/spell-data-source"; import type { CooldownDuration, DS4SpellDataSourceData } from '../../../../src/documents/item/spell/spell-data-source';
const defaultData: DS4SpellDataSourceData = { const defaultData: DS4SpellDataSourceData = {
description: "", description: '',
equipped: false, equipped: false,
spellType: "spellcasting", spellType: 'spellcasting',
spellModifier: { spellModifier: {
numerical: 0, numerical: 0,
complex: "", complex: '',
}, },
allowsDefense: false, allowsDefense: false,
spellGroups: { spellGroups: {
@ -37,18 +37,18 @@ const defaultData: DS4SpellDataSourceData = {
area: false, area: false,
}, },
maxDistance: { maxDistance: {
value: "", value: '',
unit: "meter", unit: 'meter',
}, },
effectRadius: { effectRadius: {
value: "", value: '',
unit: "meter", unit: 'meter',
}, },
duration: { duration: {
value: "", value: '',
unit: "custom", unit: 'custom',
}, },
cooldownDuration: "0r", cooldownDuration: '0r',
minimumLevels: { minimumLevels: {
healer: null, healer: null,
wizard: null, wizard: null,
@ -62,12 +62,12 @@ type TestCase = {
}; };
type CombinedTestCase = { type CombinedTestCase = {
minimumLevels: DS4SpellDataSourceData["minimumLevels"]; minimumLevels: DS4SpellDataSourceData['minimumLevels'];
expected: number | null; expected: number | null;
description: string; description: string;
}; };
const testCases: Record<keyof DS4SpellDataSourceData["minimumLevels"], TestCase[]> = { const testCases: Record<keyof DS4SpellDataSourceData['minimumLevels'], TestCase[]> = {
healer: [ healer: [
{ minimumLevel: null, expected: null }, { minimumLevel: null, expected: null },
{ minimumLevel: 1, expected: 10 }, { minimumLevel: 1, expected: 10 },
@ -150,9 +150,7 @@ function buildCombinedTestCases(): CombinedTestCase[] {
for (const sorcererTestCase of testCases.sorcerer.filter(isRelevantPermutationTestCase)) { for (const sorcererTestCase of testCases.sorcerer.filter(isRelevantPermutationTestCase)) {
for (const wizardTestCase of testCases.wizard.filter(isRelevantPermutationTestCase)) { for (const wizardTestCase of testCases.wizard.filter(isRelevantPermutationTestCase)) {
const expected = const expected =
healerTestCase.expected !== null || healerTestCase.expected !== null || sorcererTestCase.expected !== null || wizardTestCase.expected !== null
sorcererTestCase.expected !== null ||
wizardTestCase.expected !== null
? Math.min( ? Math.min(
healerTestCase.expected ?? Infinity, healerTestCase.expected ?? Infinity,
sorcererTestCase.expected ?? Infinity, sorcererTestCase.expected ?? Infinity,
@ -177,7 +175,7 @@ function buildCombinedTestCases(): CombinedTestCase[] {
// single test cases // single test cases
const isRelevantSingleTestCase = (t: TestCase) => t.minimumLevel !== null; const isRelevantSingleTestCase = (t: TestCase) => t.minimumLevel !== null;
for (const spellCasterClass of ["healer", "sorcerer", "wizard"] as const) { for (const spellCasterClass of ['healer', 'sorcerer', 'wizard'] as const) {
for (const testCase of testCases[spellCasterClass].filter(isRelevantSingleTestCase)) { for (const testCase of testCases[spellCasterClass].filter(isRelevantSingleTestCase)) {
const minimumLevels = { const minimumLevels = {
...defaultData.minimumLevels, ...defaultData.minimumLevels,
@ -195,20 +193,20 @@ function buildCombinedTestCases(): CombinedTestCase[] {
return combinedTestCases; return combinedTestCases;
} }
describe("calculateSpellPrice", () => { describe('calculateSpellPrice', () => {
const cooldownDurations: { cooldownDuration: CooldownDuration; factor: number }[] = [ const cooldownDurations: { cooldownDuration: CooldownDuration; factor: number }[] = [
{ cooldownDuration: "0r", factor: 1 }, { cooldownDuration: '0r', factor: 1 },
{ cooldownDuration: "1r", factor: 1 }, { cooldownDuration: '1r', factor: 1 },
{ cooldownDuration: "2r", factor: 1 }, { cooldownDuration: '2r', factor: 1 },
{ cooldownDuration: "5r", factor: 1 }, { cooldownDuration: '5r', factor: 1 },
{ cooldownDuration: "10r", factor: 1 }, { cooldownDuration: '10r', factor: 1 },
{ cooldownDuration: "100r", factor: 1 }, { cooldownDuration: '100r', factor: 1 },
{ cooldownDuration: "1d", factor: 2 }, { cooldownDuration: '1d', factor: 2 },
{ cooldownDuration: "d20d", factor: 3 }, { cooldownDuration: 'd20d', factor: 3 },
]; ];
describe.each(cooldownDurations)( describe.each(cooldownDurations)(
"with cooldown duration set to $cooldownDuration", 'with cooldown duration set to $cooldownDuration',
({ cooldownDuration, factor }) => { ({ cooldownDuration, factor }) => {
const dataWithCooldownDuration = { const dataWithCooldownDuration = {
...defaultData, ...defaultData,

View file

@ -2,12 +2,12 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { describe, expect, it } from "vitest"; import { describe, expect, it } from 'vitest';
import { defaultEvaluator, Evaluator, mathEvaluator } from "../../src/expression-evaluation/evaluator"; import { defaultEvaluator, Evaluator, mathEvaluator } from '../../src/expression-evaluation/evaluator';
describe("Evaluator", () => { describe('Evaluator', () => {
it("evaluates expressions that only use identifiers according to the given predicate", () => { it('evaluates expressions that only use identifiers according to the given predicate', () => {
// given // given
const expression = "typeof 'foo' === 'string' ? 42 : null"; const expression = "typeof 'foo' === 'string' ? 42 : null";
@ -18,7 +18,7 @@ describe("Evaluator", () => {
expect(result).toEqual(42); expect(result).toEqual(42);
}); });
it("fails to evaluate expressions that contain identifiers that are not allowed by the predicate", () => { it('fails to evaluate expressions that contain identifiers that are not allowed by the predicate', () => {
// given // given
const expression = "typeof 'foo' === 'string' ? 42 : function (){}"; const expression = "typeof 'foo' === 'string' ? 42 : function (){}";
@ -29,33 +29,33 @@ describe("Evaluator", () => {
expect(evaluate).toThrowError("'function' is not an allowed identifier."); expect(evaluate).toThrowError("'function' is not an allowed identifier.");
}); });
it("fails to evaluate expressions that contain invalid tokens", () => { it('fails to evaluate expressions that contain invalid tokens', () => {
// given // given
const expression = "1;"; const expression = '1;';
// when // when
const evaluate = () => defaultEvaluator.evaluate(expression); const evaluate = () => defaultEvaluator.evaluate(expression);
// then // then
expect(evaluate).toThrowError("Invalid or unexpected token (1)"); expect(evaluate).toThrowError('Invalid or unexpected token (1)');
}); });
it("fails to evaluate expressions that contain arrow functions", () => { it('fails to evaluate expressions that contain arrow functions', () => {
// given // given
const expression = "(() => 1)()"; const expression = '(() => 1)()';
// when // when
const evaluate = () => defaultEvaluator.evaluate(expression); const evaluate = () => defaultEvaluator.evaluate(expression);
// then // then
expect(evaluate).toThrowError("Invalid or unexpected token (4)"); expect(evaluate).toThrowError('Invalid or unexpected token (4)');
}); });
it("makes the given context available", () => { it('makes the given context available', () => {
// given // given
const context = { floor: Math.floor }; const context = { floor: Math.floor };
const evaluator = new Evaluator({ context }); const evaluator = new Evaluator({ context });
const expression = "floor(0.5)"; const expression = 'floor(0.5)';
// when // when
const result = evaluator.evaluate(expression); const result = evaluator.evaluate(expression);
@ -64,10 +64,10 @@ describe("Evaluator", () => {
expect(result).toEqual(0); expect(result).toEqual(0);
}); });
describe("mathEvaluator", () => { describe('mathEvaluator', () => {
it("makes the given context available", () => { it('makes the given context available', () => {
// given // given
const expression = "sqrt(sin(PI))"; const expression = 'sqrt(sin(PI))';
// when // when
const result = mathEvaluator.evaluate(expression); const result = mathEvaluator.evaluate(expression);
@ -76,9 +76,9 @@ describe("Evaluator", () => {
expect(result).toEqual(Math.sqrt(Math.sin(Math.PI))); expect(result).toEqual(Math.sqrt(Math.sin(Math.PI)));
}); });
it("does not give acces to the function constructor", () => { it('does not give acces to the function constructor', () => {
// given // given
const expression = "sqrt.constructor"; const expression = 'sqrt.constructor';
// when // when
const evaluate = () => mathEvaluator.evaluate(expression); const evaluate = () => mathEvaluator.evaluate(expression);

View file

@ -2,574 +2,574 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { describe, expect, it } from "vitest"; import { describe, expect, it } from 'vitest';
import { Lexer } from "../../src/expression-evaluation/lexer"; import { Lexer } from '../../src/expression-evaluation/lexer';
import type { Token } from "../../src/expression-evaluation/grammar"; import type { Token } from '../../src/expression-evaluation/grammar';
describe("Lexer", () => { describe('Lexer', () => {
const singleOperatorTestCases: { input: string; expected: Token[] }[] = [ const singleOperatorTestCases: { input: string; expected: Token[] }[] = [
{ {
input: "+", input: '+',
expected: [ expected: [
{ type: "+", pos: 0 }, { type: '+', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "-", input: '-',
expected: [ expected: [
{ type: "-", pos: 0 }, { type: '-', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "*", input: '*',
expected: [ expected: [
{ type: "*", pos: 0 }, { type: '*', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "**", input: '**',
expected: [ expected: [
{ type: "**", pos: 0 }, { type: '**', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "/", input: '/',
expected: [ expected: [
{ type: "/", pos: 0 }, { type: '/', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "%", input: '%',
expected: [ expected: [
{ type: "%", pos: 0 }, { type: '%', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "===", input: '===',
expected: [ expected: [
{ type: "===", pos: 0 }, { type: '===', pos: 0 },
{ type: "eof", pos: 3 }, { type: 'eof', pos: 3 },
], ],
}, },
{ {
input: "!==", input: '!==',
expected: [ expected: [
{ type: "!==", pos: 0 }, { type: '!==', pos: 0 },
{ type: "eof", pos: 3 }, { type: 'eof', pos: 3 },
], ],
}, },
{ {
input: "==", input: '==',
expected: [ expected: [
{ type: "==", pos: 0 }, { type: '==', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "<", input: '<',
expected: [ expected: [
{ type: "<", pos: 0 }, { type: '<', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "<=", input: '<=',
expected: [ expected: [
{ type: "<=", pos: 0 }, { type: '<=', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: ">", input: '>',
expected: [ expected: [
{ type: ">", pos: 0 }, { type: '>', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: ">=", input: '>=',
expected: [ expected: [
{ type: ">=", pos: 0 }, { type: '>=', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "&&", input: '&&',
expected: [ expected: [
{ type: "&&", pos: 0 }, { type: '&&', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "||", input: '||',
expected: [ expected: [
{ type: "||", pos: 0 }, { type: '||', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "&", input: '&',
expected: [ expected: [
{ type: "&", pos: 0 }, { type: '&', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "|", input: '|',
expected: [ expected: [
{ type: "|", pos: 0 }, { type: '|', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "<<", input: '<<',
expected: [ expected: [
{ type: "<<", pos: 0 }, { type: '<<', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: ">>>", input: '>>>',
expected: [ expected: [
{ type: ">>>", pos: 0 }, { type: '>>>', pos: 0 },
{ type: "eof", pos: 3 }, { type: 'eof', pos: 3 },
], ],
}, },
{ {
input: ".", input: '.',
expected: [ expected: [
{ type: ".", pos: 0 }, { type: '.', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "?.", input: '?.',
expected: [ expected: [
{ type: "?.", pos: 0 }, { type: '?.', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "??", input: '??',
expected: [ expected: [
{ type: "??", pos: 0 }, { type: '??', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "?", input: '?',
expected: [ expected: [
{ type: "?", pos: 0 }, { type: '?', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: ":", input: ':',
expected: [ expected: [
{ type: ":", pos: 0 }, { type: ':', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "(", input: '(',
expected: [ expected: [
{ type: "(", pos: 0 }, { type: '(', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: ")", input: ')',
expected: [ expected: [
{ type: ")", pos: 0 }, { type: ')', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "[", input: '[',
expected: [ expected: [
{ type: "[", pos: 0 }, { type: '[', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "]", input: ']',
expected: [ expected: [
{ type: "]", pos: 0 }, { type: ']', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: ",", input: ',',
expected: [ expected: [
{ type: ",", pos: 0 }, { type: ',', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "{", input: '{',
expected: [ expected: [
{ type: "{", pos: 0 }, { type: '{', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "}", input: '}',
expected: [ expected: [
{ type: "}", pos: 0 }, { type: '}', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
]; ];
const singleNumberTestCases: { input: string; expected: Token[] }[] = [ const singleNumberTestCases: { input: string; expected: Token[] }[] = [
{ {
input: "1", input: '1',
expected: [ expected: [
{ type: "number", symbol: "1", pos: 0 }, { type: 'number', symbol: '1', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "42", input: '42',
expected: [ expected: [
{ type: "number", symbol: "42", pos: 0 }, { type: 'number', symbol: '42', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "42.9", input: '42.9',
expected: [ expected: [
{ type: "number", symbol: "42.9", pos: 0 }, { type: 'number', symbol: '42.9', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: ".9", input: '.9',
expected: [ expected: [
{ type: "number", symbol: ".9", pos: 0 }, { type: 'number', symbol: '.9', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "1_1", input: '1_1',
expected: [ expected: [
{ type: "number", symbol: "1_1", pos: 0 }, { type: 'number', symbol: '1_1', pos: 0 },
{ type: "eof", pos: 3 }, { type: 'eof', pos: 3 },
], ],
}, },
{ {
input: "10_1", input: '10_1',
expected: [ expected: [
{ type: "number", symbol: "10_1", pos: 0 }, { type: 'number', symbol: '10_1', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "1_1_1", input: '1_1_1',
expected: [ expected: [
{ type: "number", symbol: "1_1_1", pos: 0 }, { type: 'number', symbol: '1_1_1', pos: 0 },
{ type: "eof", pos: 5 }, { type: 'eof', pos: 5 },
], ],
}, },
{ {
input: ".1_1", input: '.1_1',
expected: [ expected: [
{ type: "number", symbol: ".1_1", pos: 0 }, { type: 'number', symbol: '.1_1', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
]; ];
const invalidNumberTestCases: { input: string; expected: Token[] }[] = [ const invalidNumberTestCases: { input: string; expected: Token[] }[] = [
{ input: "1.1.1", expected: [{ type: "invalid", pos: 0 }] }, { input: '1.1.1', expected: [{ type: 'invalid', pos: 0 }] },
{ input: "1__1", expected: [{ type: "invalid", pos: 0 }] }, { input: '1__1', expected: [{ type: 'invalid', pos: 0 }] },
{ input: "1_", expected: [{ type: "invalid", pos: 0 }] }, { input: '1_', expected: [{ type: 'invalid', pos: 0 }] },
{ input: "1._1", expected: [{ type: "invalid", pos: 0 }] }, { input: '1._1', expected: [{ type: 'invalid', pos: 0 }] },
{ input: "0_1", expected: [{ type: "invalid", pos: 0 }] }, { input: '0_1', expected: [{ type: 'invalid', pos: 0 }] },
{ input: "00_1", expected: [{ type: "invalid", pos: 0 }] }, { input: '00_1', expected: [{ type: 'invalid', pos: 0 }] },
]; ];
const singleIdentifierTestCases: { input: string; expected: Token[] }[] = [ const singleIdentifierTestCases: { input: string; expected: Token[] }[] = [
{ {
input: "foo", input: 'foo',
expected: [ expected: [
{ type: "iden", symbol: "foo", pos: 0 }, { type: 'iden', symbol: 'foo', pos: 0 },
{ type: "eof", pos: 3 }, { type: 'eof', pos: 3 },
], ],
}, },
{ {
input: "_foo", input: '_foo',
expected: [ expected: [
{ type: "iden", symbol: "_foo", pos: 0 }, { type: 'iden', symbol: '_foo', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "$foo", input: '$foo',
expected: [ expected: [
{ type: "iden", symbol: "$foo", pos: 0 }, { type: 'iden', symbol: '$foo', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "foo1", input: 'foo1',
expected: [ expected: [
{ type: "iden", symbol: "foo1", pos: 0 }, { type: 'iden', symbol: 'foo1', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "_foo1_", input: '_foo1_',
expected: [ expected: [
{ type: "iden", symbol: "_foo1_", pos: 0 }, { type: 'iden', symbol: '_foo1_', pos: 0 },
{ type: "eof", pos: 6 }, { type: 'eof', pos: 6 },
], ],
}, },
{ {
input: "μ", input: 'μ',
expected: [ expected: [
{ type: "iden", symbol: "μ", pos: 0 }, { type: 'iden', symbol: 'μ', pos: 0 },
{ type: "eof", pos: 1 }, { type: 'eof', pos: 1 },
], ],
}, },
{ {
input: "._1", input: '._1',
expected: [ expected: [
{ type: ".", pos: 0 }, { type: '.', pos: 0 },
{ type: "iden", symbol: "_1", pos: 1 }, { type: 'iden', symbol: '_1', pos: 1 },
{ type: "eof", pos: 3 }, { type: 'eof', pos: 3 },
], ],
}, },
{ {
input: "true", input: 'true',
expected: [ expected: [
{ type: "iden", symbol: "true", pos: 0 }, { type: 'iden', symbol: 'true', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "false", input: 'false',
expected: [ expected: [
{ type: "iden", symbol: "false", pos: 0 }, { type: 'iden', symbol: 'false', pos: 0 },
{ type: "eof", pos: 5 }, { type: 'eof', pos: 5 },
], ],
}, },
{ {
input: "null", input: 'null',
expected: [ expected: [
{ type: "iden", symbol: "null", pos: 0 }, { type: 'iden', symbol: 'null', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "undefined", input: 'undefined',
expected: [ expected: [
{ type: "iden", symbol: "undefined", pos: 0 }, { type: 'iden', symbol: 'undefined', pos: 0 },
{ type: "eof", pos: 9 }, { type: 'eof', pos: 9 },
], ],
}, },
]; ];
const invalidIdentifierTestCases: { input: string; expected: Token[] }[] = [ const invalidIdentifierTestCases: { input: string; expected: Token[] }[] = [
{ {
input: "1foo", input: '1foo',
expected: [ expected: [
{ type: "number", symbol: "1", pos: 0 }, { type: 'number', symbol: '1', pos: 0 },
{ type: "iden", symbol: "foo", pos: 1 }, { type: 'iden', symbol: 'foo', pos: 1 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ input: "↓", expected: [{ type: "invalid", pos: 0 }] }, { input: '↓', expected: [{ type: 'invalid', pos: 0 }] },
{ input: '"', expected: [{ type: "invalid", pos: 0 }] }, { input: '"', expected: [{ type: 'invalid', pos: 0 }] },
]; ];
const singleStringTestCases: { input: string; expected: Token[] }[] = [ const singleStringTestCases: { input: string; expected: Token[] }[] = [
{ {
input: '""', input: '""',
expected: [ expected: [
{ type: "string", symbol: '""', pos: 0 }, { type: 'string', symbol: '""', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: '"foo"', input: '"foo"',
expected: [ expected: [
{ type: "string", symbol: '"foo"', pos: 0 }, { type: 'string', symbol: '"foo"', pos: 0 },
{ type: "eof", pos: 5 }, { type: 'eof', pos: 5 },
], ],
}, },
{ {
input: '"\\""', input: '"\\""',
expected: [ expected: [
{ type: "string", symbol: '"\\""', pos: 0 }, { type: 'string', symbol: '"\\""', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: '"\\\'"', input: '"\\\'"',
expected: [ expected: [
{ type: "string", symbol: '"\\\'"', pos: 0 }, { type: 'string', symbol: '"\\\'"', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "''", input: "''",
expected: [ expected: [
{ type: "string", symbol: "''", pos: 0 }, { type: 'string', symbol: "''", pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "'foo'", input: "'foo'",
expected: [ expected: [
{ type: "string", symbol: "'foo'", pos: 0 }, { type: 'string', symbol: "'foo'", pos: 0 },
{ type: "eof", pos: 5 }, { type: 'eof', pos: 5 },
], ],
}, },
{ {
input: "'\\''", input: "'\\''",
expected: [ expected: [
{ type: "string", symbol: "'\\''", pos: 0 }, { type: 'string', symbol: "'\\''", pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "'\\\"'", input: "'\\\"'",
expected: [ expected: [
{ type: "string", symbol: "'\\\"'", pos: 0 }, { type: 'string', symbol: "'\\\"'", pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: "``", input: '``',
expected: [ expected: [
{ type: "string", symbol: "``", pos: 0 }, { type: 'string', symbol: '``', pos: 0 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "`foo`", input: '`foo`',
expected: [ expected: [
{ type: "string", symbol: "`foo`", pos: 0 }, { type: 'string', symbol: '`foo`', pos: 0 },
{ type: "eof", pos: 5 }, { type: 'eof', pos: 5 },
], ],
}, },
{ {
input: "`\\``", input: '`\\``',
expected: [ expected: [
{ type: "string", symbol: "`\\``", pos: 0 }, { type: 'string', symbol: '`\\``', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
{ {
input: '`\\"`', input: '`\\"`',
expected: [ expected: [
{ type: "string", symbol: '`\\"`', pos: 0 }, { type: 'string', symbol: '`\\"`', pos: 0 },
{ type: "eof", pos: 4 }, { type: 'eof', pos: 4 },
], ],
}, },
]; ];
const invalidStringTestCases: { input: string; expected: Token[] }[] = [ const invalidStringTestCases: { input: string; expected: Token[] }[] = [
{ input: '"', expected: [{ type: "invalid", pos: 0 }] }, { input: '"', expected: [{ type: 'invalid', pos: 0 }] },
{ input: '"\\"', expected: [{ type: "invalid", pos: 0 }] }, { input: '"\\"', expected: [{ type: 'invalid', pos: 0 }] },
{ input: "'", expected: [{ type: "invalid", pos: 0 }] }, { input: "'", expected: [{ type: 'invalid', pos: 0 }] },
{ input: "'\\'", expected: [{ type: "invalid", pos: 0 }] }, { input: "'\\'", expected: [{ type: 'invalid', pos: 0 }] },
]; ];
const whiteSpaceTestCases: { input: string; expected: Token[] }[] = [ const whiteSpaceTestCases: { input: string; expected: Token[] }[] = [
{ input: " ", expected: [{ type: "eof", pos: 1 }] }, { input: ' ', expected: [{ type: 'eof', pos: 1 }] },
{ input: " ", expected: [{ type: "eof", pos: 3 }] }, { input: ' ', expected: [{ type: 'eof', pos: 3 }] },
{ input: "\n", expected: [{ type: "eof", pos: 1 }] }, { input: '\n', expected: [{ type: 'eof', pos: 1 }] },
{ input: " \n", expected: [{ type: "eof", pos: 2 }] }, { input: ' \n', expected: [{ type: 'eof', pos: 2 }] },
{ input: " ", expected: [{ type: "eof", pos: 1 }] }, { input: ' ', expected: [{ type: 'eof', pos: 1 }] },
]; ];
const complicatedTermTestCases: { input: string; expected: Token[] }[] = [ const complicatedTermTestCases: { input: string; expected: Token[] }[] = [
{ {
input: "5x", input: '5x',
expected: [ expected: [
{ type: "number", symbol: "5", pos: 0 }, { type: 'number', symbol: '5', pos: 0 },
{ type: "iden", symbol: "x", pos: 1 }, { type: 'iden', symbol: 'x', pos: 1 },
{ type: "eof", pos: 2 }, { type: 'eof', pos: 2 },
], ],
}, },
{ {
input: "5*x", input: '5*x',
expected: [ expected: [
{ type: "number", symbol: "5", pos: 0 }, { type: 'number', symbol: '5', pos: 0 },
{ type: "*", pos: 1 }, { type: '*', pos: 1 },
{ type: "iden", symbol: "x", pos: 2 }, { type: 'iden', symbol: 'x', pos: 2 },
{ type: "eof", pos: 3 }, { type: 'eof', pos: 3 },
], ],
}, },
{ {
input: "5 * x", input: '5 * x',
expected: [ expected: [
{ type: "number", symbol: "5", pos: 0 }, { type: 'number', symbol: '5', pos: 0 },
{ type: "*", pos: 2 }, { type: '*', pos: 2 },
{ type: "iden", symbol: "x", pos: 4 }, { type: 'iden', symbol: 'x', pos: 4 },
{ type: "eof", pos: 5 }, { type: 'eof', pos: 5 },
], ],
}, },
{ {
input: "(5 * 5 + 2) / 1.2 === 'foo'", input: "(5 * 5 + 2) / 1.2 === 'foo'",
expected: [ expected: [
{ type: "(", pos: 0 }, { type: '(', pos: 0 },
{ type: "number", symbol: "5", pos: 1 }, { type: 'number', symbol: '5', pos: 1 },
{ type: "*", pos: 3 }, { type: '*', pos: 3 },
{ type: "number", symbol: "5", pos: 5 }, { type: 'number', symbol: '5', pos: 5 },
{ type: "+", pos: 7 }, { type: '+', pos: 7 },
{ type: "number", symbol: "2", pos: 9 }, { type: 'number', symbol: '2', pos: 9 },
{ type: ")", pos: 10 }, { type: ')', pos: 10 },
{ type: "/", pos: 12 }, { type: '/', pos: 12 },
{ type: "number", symbol: "1.2", pos: 14 }, { type: 'number', symbol: '1.2', pos: 14 },
{ type: "===", pos: 18 }, { type: '===', pos: 18 },
{ type: "string", symbol: "'foo'", pos: 22 }, { type: 'string', symbol: "'foo'", pos: 22 },
{ type: "eof", pos: 27 }, { type: 'eof', pos: 27 },
], ],
}, },
{ {
input: "(() => {console.log('foo'); return 1;})()", input: "(() => {console.log('foo'); return 1;})()",
expected: [ expected: [
{ type: "(", pos: 0 }, { type: '(', pos: 0 },
{ type: "(", pos: 1 }, { type: '(', pos: 1 },
{ type: ")", pos: 2 }, { type: ')', pos: 2 },
{ type: "invalid", pos: 4 }, { type: 'invalid', pos: 4 },
], ],
}, },
{ {
input: "(function() {console.log('foo'); return 1;})()", input: "(function() {console.log('foo'); return 1;})()",
expected: [ expected: [
{ type: "(", pos: 0 }, { type: '(', pos: 0 },
{ type: "iden", symbol: "function", pos: 1 }, { type: 'iden', symbol: 'function', pos: 1 },
{ type: "(", pos: 9 }, { type: '(', pos: 9 },
{ type: ")", pos: 10 }, { type: ')', pos: 10 },
{ type: "{", pos: 12 }, { type: '{', pos: 12 },
{ type: "iden", symbol: "console", pos: 13 }, { type: 'iden', symbol: 'console', pos: 13 },
{ type: ".", pos: 20 }, { type: '.', pos: 20 },
{ type: "iden", symbol: "log", pos: 21 }, { type: 'iden', symbol: 'log', pos: 21 },
{ type: "(", pos: 24 }, { type: '(', pos: 24 },
{ type: "string", symbol: "'foo'", pos: 25 }, { type: 'string', symbol: "'foo'", pos: 25 },
{ type: ")", pos: 30 }, { type: ')', pos: 30 },
{ type: "invalid", pos: 31 }, { type: 'invalid', pos: 31 },
], ],
}, },
{ {
input: "'ranged' === 'ranged'", input: "'ranged' === 'ranged'",
expected: [ expected: [
{ type: "string", symbol: "'ranged'", pos: 0 }, { type: 'string', symbol: "'ranged'", pos: 0 },
{ type: "===", pos: 9 }, { type: '===', pos: 9 },
{ type: "string", symbol: "'ranged'", pos: 13 }, { type: 'string', symbol: "'ranged'", pos: 13 },
{ type: "eof", pos: 21 }, { type: 'eof', pos: 21 },
], ],
}, },
]; ];
@ -584,7 +584,7 @@ describe("Lexer", () => {
...invalidStringTestCases, ...invalidStringTestCases,
...whiteSpaceTestCases, ...whiteSpaceTestCases,
...complicatedTermTestCases, ...complicatedTermTestCases,
])("lexes $input correctly", ({ input, expected }) => { ])('lexes $input correctly', ({ input, expected }) => {
// when // when
const result = consume(new Lexer(input)); const result = consume(new Lexer(input));

View file

@ -2,17 +2,17 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { describe, expect, it } from "vitest"; import { describe, expect, it } from 'vitest';
import { literals, safeOperators } from "../../src/expression-evaluation/grammar"; import { literals, safeOperators } from '../../src/expression-evaluation/grammar';
import { Validator } from "../../src/expression-evaluation/validator"; import { Validator } from '../../src/expression-evaluation/validator';
describe("Validator", () => { describe('Validator', () => {
it("allows identifier according to the given predicate", () => { it('allows identifier according to the given predicate', () => {
// given // given
const predicate = (identifier: string) => identifier === "true"; const predicate = (identifier: string) => identifier === 'true';
const validator = new Validator(predicate); const validator = new Validator(predicate);
const input = "true"; const input = 'true';
// when // when
const validate = () => validator.validate(input); const validate = () => validator.validate(input);
@ -21,11 +21,11 @@ describe("Validator", () => {
expect(validate).not.toThrow(); expect(validate).not.toThrow();
}); });
it("disallows identifier according to the given predicate", () => { it('disallows identifier according to the given predicate', () => {
// given // given
const predicate = (identifier: string) => identifier === "false"; const predicate = (identifier: string) => identifier === 'false';
const validator = new Validator(predicate); const validator = new Validator(predicate);
const input = "true"; const input = 'true';
// when // when
const validate = () => validator.validate(input); const validate = () => validator.validate(input);
@ -34,11 +34,11 @@ describe("Validator", () => {
expect(validate).toThrowError("'true' is not an allowed identifier"); expect(validate).toThrowError("'true' is not an allowed identifier");
}); });
it("allows multiple identifiers according to the given predicate", () => { it('allows multiple identifiers according to the given predicate', () => {
// given // given
const predicate = (identifier: string) => identifier === "true" || identifier === "null"; const predicate = (identifier: string) => identifier === 'true' || identifier === 'null';
const validator = new Validator(predicate); const validator = new Validator(predicate);
const input = "true null"; const input = 'true null';
// when // when
const validate = () => validator.validate(input); const validate = () => validator.validate(input);
@ -47,11 +47,11 @@ describe("Validator", () => {
expect(validate).not.toThrow(); expect(validate).not.toThrow();
}); });
it("allows multiple identifiers in a more complex expression according to the given rule", () => { it('allows multiple identifiers in a more complex expression according to the given rule', () => {
// given // given
const predicate = (identifier: string) => identifier === "true" || identifier === "null"; const predicate = (identifier: string) => identifier === 'true' || identifier === 'null';
const validator = new Validator(predicate); const validator = new Validator(predicate);
const input = "true === null"; const input = 'true === null';
// when // when
const validate = () => validator.validate(input); const validate = () => validator.validate(input);
@ -60,11 +60,11 @@ describe("Validator", () => {
expect(validate).not.toThrow(); expect(validate).not.toThrow();
}); });
it("mentions the first not allowed identifier in the thrown errror", () => { it('mentions the first not allowed identifier in the thrown errror', () => {
// given // given
const predicate = (identifier: string) => identifier === "true" || identifier === "null"; const predicate = (identifier: string) => identifier === 'true' || identifier === 'null';
const validator = new Validator(predicate); const validator = new Validator(predicate);
const input = "true === null && undefined === false"; const input = 'true === null && undefined === false';
// when // when
const validate = () => validator.validate(input); const validate = () => validator.validate(input);
@ -73,22 +73,21 @@ describe("Validator", () => {
expect(validate).toThrowError("'undefined' is not an allowed identifier."); expect(validate).toThrowError("'undefined' is not an allowed identifier.");
}); });
it("disallows invalid invalid tokens", () => { it('disallows invalid invalid tokens', () => {
// given // given
const validator = new Validator(); const validator = new Validator();
const input = ";"; const input = ';';
// when // when
const validate = () => validator.validate(input); const validate = () => validator.validate(input);
// then // then
expect(validate).toThrowError("Invalid or unexpected token (0)"); expect(validate).toThrowError('Invalid or unexpected token (0)');
}); });
it("allows a complicated valid expression", () => { it('allows a complicated valid expression', () => {
// given // given
const predicate = (identifier: string) => const predicate = (identifier: string) => [...safeOperators, ...literals, 'floor', 'random'].includes(identifier);
[...safeOperators, ...literals, "floor", "random"].includes(identifier);
const validator = new Validator(predicate); const validator = new Validator(predicate);
const input = "typeof (floor(random() * 5) / 2) === 'number' ? 42 : 'foo'"; const input = "typeof (floor(random() * 5) / 2) === 'number' ? 42 : 'foo'";
@ -99,9 +98,9 @@ describe("Validator", () => {
expect(validate).not.toThrow(); expect(validate).not.toThrow();
}); });
it("disallows a complicated expression if it contains a disallowed identifier", () => { it('disallows a complicated expression if it contains a disallowed identifier', () => {
// given // given
const predicate = (identifier: string) => [...safeOperators, ...literals, "ceil"].includes(identifier); const predicate = (identifier: string) => [...safeOperators, ...literals, 'ceil'].includes(identifier);
const validator = new Validator(predicate); const validator = new Validator(predicate);
const input = "ceil.constructor('alert(1); return 1;')()"; const input = "ceil.constructor('alert(1); return 1;')()";
@ -112,15 +111,15 @@ describe("Validator", () => {
expect(validate).toThrowError("'constructor' is not an allowed identifier."); expect(validate).toThrowError("'constructor' is not an allowed identifier.");
}); });
it("disallows arrow functions", () => { it('disallows arrow functions', () => {
// given // given
const validator = new Validator(); const validator = new Validator();
const input = "() => {}"; const input = '() => {}';
// when // when
const validate = () => validator.validate(input); const validate = () => validator.validate(input);
// then // then
expect(validate).toThrowError("Invalid or unexpected token (3)"); expect(validate).toThrowError('Invalid or unexpected token (3)');
}); });
}); });

View file

@ -2,13 +2,13 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { describe, expect, it } from "vitest"; import { describe, expect, it } from 'vitest';
import de from "../../lang/de.json"; import de from '../../lang/de.json';
import en from "../../lang/en.json"; import en from '../../lang/en.json';
describe("English and german localization files", () => { describe('English and german localization files', () => {
it("should have the same keys.", () => { it('should have the same keys.', () => {
const deKeys = Object.keys(de); const deKeys = Object.keys(de);
const enKeys = Object.keys(en); const enKeys = Object.keys(en);
expect(deKeys).toEqual(enKeys); expect(deKeys).toEqual(enKeys);

View file

@ -2,23 +2,23 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import en from "../lang/en.json"; import en from '../lang/en.json';
function setupPrimitives() { function setupPrimitives() {
Object.defineProperties(Number, { Object.defineProperties(Number, {
isNumeric: { isNumeric: {
value: function (n: unknown) { value: function (n: unknown) {
if (n instanceof Array) return false; if (n instanceof Array) return false;
else if (([null, ""] as unknown[]).includes(n)) return false; else if (([null, ''] as unknown[]).includes(n)) return false;
// @ts-expect-error Abusing JavaScript a bit here, but it's the implementation from foundry // @ts-expect-error Abusing JavaScript a bit here, but it's the implementation from foundry
return +n === +n; return +n === +n;
}, },
}, },
fromString: { fromString: {
value: function (str: unknown) { value: function (str: unknown) {
if (typeof str !== "string" || !str.length) return NaN; if (typeof str !== 'string' || !str.length) return NaN;
// Remove whitespace. // Remove whitespace.
str = str.replace(/\s+/g, ""); str = str.replace(/\s+/g, '');
return Number(str); return Number(str);
}, },
}, },

View file

@ -6,7 +6,7 @@ export class DS4ActiveEffectConfig extends ActiveEffectConfig {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
template: "systems/ds4/templates/sheets/active-effect/active-effect-config.hbs", template: 'systems/ds4/templates/sheets/active-effect/active-effect-config.hbs',
}); });
} }
@ -17,7 +17,7 @@ export class DS4ActiveEffectConfig extends ActiveEffectConfig {
activateListeners(html) { activateListeners(html) {
super.activateListeners(html); super.activateListeners(html);
const checkbox = html[0]?.querySelector('input[name="flags.ds4.itemEffectConfig.applyToItems"]'); const checkbox = html[0]?.querySelector('input[name="flags.ds4.itemEffectConfig.applyToItems"]');
checkbox?.addEventListener("change", () => this.#toggleItemEffectConfig(checkbox.checked)); checkbox?.addEventListener('change', () => this.#toggleItemEffectConfig(checkbox.checked));
} }
/** /**
@ -25,14 +25,14 @@ export class DS4ActiveEffectConfig extends ActiveEffectConfig {
* @param {boolean} active The target state * @param {boolean} active The target state
*/ */
#toggleItemEffectConfig(active) { #toggleItemEffectConfig(active) {
const elements = this.element[0]?.querySelectorAll(".ds4-item-effect-config"); const elements = this.element[0]?.querySelectorAll('.ds4-item-effect-config');
elements?.forEach((element) => { elements?.forEach((element) => {
if (active) { if (active) {
element.classList.remove("ds4-hidden"); element.classList.remove('ds4-hidden');
} else { } else {
element.classList.add("ds4-hidden"); element.classList.add('ds4-hidden');
} }
}); });
this.setPosition({ height: "auto" }); this.setPosition({ height: 'auto' });
} }
} }

View file

@ -5,13 +5,13 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4 } from "../../config"; import { DS4 } from '../../config';
import { DS4ActiveEffect } from "../../documents/active-effect"; import { DS4ActiveEffect } from '../../documents/active-effect';
import { isCheck } from "../../documents/actor/actor-data-properties-base"; import { isCheck } from '../../documents/actor/actor-data-properties-base';
import { getDS4Settings } from "../../settings"; import { getDS4Settings } from '../../settings';
import { notifications } from "../../ui/notifications"; import { notifications } from '../../ui/notifications';
import { enforce, getCanvas, getGame } from "../../utils/utils"; import { enforce, getCanvas, getGame } from '../../utils/utils';
import { disableOverriddenFields } from "../sheet-helpers"; import { disableOverriddenFields } from '../sheet-helpers';
/** /**
* The base sheet class for all {@link DS4Actor}s. * The base sheet class for all {@link DS4Actor}s.
@ -20,14 +20,14 @@ export class DS4ActorSheet extends ActorSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["sheet", "ds4-actor-sheet"], classes: ['sheet', 'ds4-actor-sheet'],
height: 645, height: 645,
scrollY: [".ds4-sheet-body"], scrollY: ['.ds4-sheet-body'],
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "values" }], tabs: [{ navSelector: '.ds4-sheet-tab-nav', contentSelector: '.ds4-sheet-body', initial: 'values' }],
dragDrop: [ dragDrop: [
{ dragSelector: ".item-list .item", dropSelector: null }, { dragSelector: '.item-list .item', dropSelector: null },
{ dragSelector: ".effect-list .effect", dropSelector: null }, { dragSelector: '.effect-list .effect', dropSelector: null },
{ dragSelector: ".ds4-check", dropSelector: null }, { dragSelector: '.ds4-check', dropSelector: null },
], ],
width: 650, width: 650,
}); });
@ -35,7 +35,7 @@ export class DS4ActorSheet extends ActorSheet {
/** @override */ /** @override */
get template() { get template() {
const basePath = "systems/ds4/templates/sheets/actor"; const basePath = 'systems/ds4/templates/sheets/actor';
if (!getGame().user?.isGM && this.actor.limited) return `${basePath}/limited-sheet.hbs`; if (!getGame().user?.isGM && this.actor.limited) return `${basePath}/limited-sheet.hbs`;
return `${basePath}/${this.actor.type}-sheet.hbs`; return `${basePath}/${this.actor.type}-sheet.hbs`;
} }
@ -74,11 +74,7 @@ export class DS4ActorSheet extends ActorSheet {
* @protected * @protected
*/ */
addTooltipsToData(context) { addTooltipsToData(context) {
const valueGroups = [ const valueGroups = [context.data.system.attributes, context.data.system.traits, context.data.system.combatValues];
context.data.system.attributes,
context.data.system.traits,
context.data.system.combatValues,
];
valueGroups.forEach((valueGroup) => { valueGroups.forEach((valueGroup) => {
Object.values(valueGroup).forEach((attribute) => { Object.values(valueGroup).forEach((attribute) => {
@ -95,9 +91,9 @@ export class DS4ActorSheet extends ActorSheet {
* @protected * @protected
*/ */
getTooltipForValue(value) { getTooltipForValue(value) {
return `${value.base} (${getGame().i18n.localize("DS4.TooltipBaseValue")}) + ${ return `${value.base} (${getGame().i18n.localize('DS4.TooltipBaseValue')}) + ${
value.mod value.mod
} (${getGame().i18n.localize("DS4.TooltipModifier")}) ${getGame().i18n.localize("DS4.TooltipEffects")} ${ } (${getGame().i18n.localize('DS4.TooltipModifier')}) ${getGame().i18n.localize('DS4.TooltipEffects')} ${
value.total value.total
}`; }`;
} }
@ -111,16 +107,16 @@ export class DS4ActorSheet extends ActorSheet {
if (!this.options.editable) return; if (!this.options.editable) return;
html.find(".control-item").on("click", this.onControlItem.bind(this)); html.find('.control-item').on('click', this.onControlItem.bind(this));
html.find(".change-item").on("change", this.onChangeItem.bind(this)); html.find('.change-item').on('change', this.onChangeItem.bind(this));
html.find(".control-effect").on("click", this.onControlEffect.bind(this)); html.find('.control-effect').on('click', this.onControlEffect.bind(this));
html.find(".change-effect").on("change", this.onChangeEffect.bind(this)); html.find('.change-effect').on('change', this.onChangeEffect.bind(this));
html.find(".rollable-item").on("click", this.onRollItem.bind(this)); html.find('.rollable-item').on('click', this.onRollItem.bind(this));
html.find(".rollable-check").on("click", this.onRollCheck.bind(this)); html.find('.rollable-check').on('click', this.onRollCheck.bind(this));
html.find(".sort-items").on("click", this.onSortItems.bind(this)); html.find('.sort-items').on('click', this.onSortItems.bind(this));
disableOverriddenFields(this.form, this.actor.overrides, (key) => `[name="${key}"]`); disableOverriddenFields(this.form, this.actor.overrides, (key) => `[name="${key}"]`);
for (const item of this.actor.items) { for (const item of this.actor.items) {
@ -141,12 +137,12 @@ export class DS4ActorSheet extends ActorSheet {
onControlItem(event) { onControlItem(event) {
event.preventDefault(); event.preventDefault();
const a = event.currentTarget; const a = event.currentTarget;
switch (a.dataset["action"]) { switch (a.dataset['action']) {
case "create": case 'create':
return this.onCreateItem(event); return this.onCreateItem(event);
case "edit": case 'edit':
return this.onEditItem(event); return this.onEditItem(event);
case "delete": case 'delete':
return this.onDeleteItem(event); return this.onDeleteItem(event);
} }
} }
@ -176,7 +172,7 @@ export class DS4ActorSheet extends ActorSheet {
const item = await fromUuid(uuid); const item = await fromUuid(uuid);
enforce( enforce(
item && item.parent === this.actor, item && item.parent === this.actor,
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }), getGame().i18n.format('DS4.ErrorActorDoesNotHaveItem', { uuid, actor: this.actor.name }),
); );
item.sheet?.render(true); item.sheet?.render(true);
} }
@ -193,7 +189,7 @@ export class DS4ActorSheet extends ActorSheet {
const item = await fromUuid(uuid); const item = await fromUuid(uuid);
enforce( enforce(
item && item.parent === this.actor, item && item.parent === this.actor,
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }), getGame().i18n.format('DS4.ErrorActorDoesNotHaveItem', { uuid, actor: this.actor.name }),
); );
item.delete(); item.delete();
$(li).slideUp(200, () => this.render(false)); $(li).slideUp(200, () => this.render(false));
@ -207,7 +203,7 @@ export class DS4ActorSheet extends ActorSheet {
* @protected * @protected
*/ */
onChangeItem(event) { onChangeItem(event) {
return this.onChangeEmbeddedDocument(event, "Item"); return this.onChangeEmbeddedDocument(event, 'Item');
} }
/** /**
@ -220,12 +216,12 @@ export class DS4ActorSheet extends ActorSheet {
onControlEffect(event) { onControlEffect(event) {
event.preventDefault(); event.preventDefault();
const a = event.currentTarget; const a = event.currentTarget;
switch (a.dataset["action"]) { switch (a.dataset['action']) {
case "create": case 'create':
return this.onCreateEffect(); return this.onCreateEffect();
case "edit": case 'edit':
return this.onEditEffect(event); return this.onEditEffect(event);
case "delete": case 'delete':
return this.onDeleteEffect(event); return this.onDeleteEffect(event);
} }
} }
@ -252,7 +248,7 @@ export class DS4ActorSheet extends ActorSheet {
const effect = await fromUuid(uuid); const effect = await fromUuid(uuid);
enforce( enforce(
effect && (effect.parent === this.actor || effect.parent.parent === this.actor), effect && (effect.parent === this.actor || effect.parent.parent === this.actor),
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }), getGame().i18n.format('DS4.ErrorActorDoesNotHaveEffect', { uuid, actor: this.actor.name }),
); );
effect.sheet?.render(true); effect.sheet?.render(true);
} }
@ -269,7 +265,7 @@ export class DS4ActorSheet extends ActorSheet {
const effect = await fromUuid(uuid); const effect = await fromUuid(uuid);
enforce( enforce(
effect && (effect.parent === this.actor || effect.parent.parent === this.actor), effect && (effect.parent === this.actor || effect.parent.parent === this.actor),
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }), getGame().i18n.format('DS4.ErrorActorDoesNotHaveEffect', { uuid, actor: this.actor.name }),
); );
effect.delete(); effect.delete();
$(li).slideUp(200, () => this.render(false)); $(li).slideUp(200, () => this.render(false));
@ -283,7 +279,7 @@ export class DS4ActorSheet extends ActorSheet {
* @protected * @protected
*/ */
onChangeEffect(event) { onChangeEffect(event) {
return this.onChangeEmbeddedDocument(event, "ActiveEffect"); return this.onChangeEmbeddedDocument(event, 'ActiveEffect');
} }
/** /**
@ -301,22 +297,22 @@ export class DS4ActorSheet extends ActorSheet {
const documentElement = element.closest(embeddedDocumentListEntryProperties[documentName].selector); const documentElement = element.closest(embeddedDocumentListEntryProperties[documentName].selector);
const uuid = documentElement.dataset[embeddedDocumentListEntryProperties[documentName].uuidDataAttribute]; const uuid = documentElement.dataset[embeddedDocumentListEntryProperties[documentName].uuidDataAttribute];
const property = element.dataset["property"]; const property = element.dataset['property'];
enforce(property !== undefined, TypeError("HTML element does not provide 'data-property' attribute")); enforce(property !== undefined, TypeError("HTML element does not provide 'data-property' attribute"));
const newValue = this.parseValue(element); const newValue = this.parseValue(element);
const document = await fromUuid(uuid); const document = await fromUuid(uuid);
if (documentName === "Item") { if (documentName === 'Item') {
enforce( enforce(
document && document.parent === this.actor, document && document.parent === this.actor,
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }), getGame().i18n.format('DS4.ErrorActorDoesNotHaveItem', { uuid, actor: this.actor.name }),
); );
} else { } else {
enforce( enforce(
document && (document.parent === this.actor || document.parent.parent === this.actor), document && (document.parent === this.actor || document.parent.parent === this.actor),
getGame().i18n.format("DS4.ErrorActorDoesNotHaveEffect", { uuid, actor: this.actor.name }), getGame().i18n.format('DS4.ErrorActorDoesNotHaveEffect', { uuid, actor: this.actor.name }),
); );
} }
document.update({ [property]: newValue }); document.update({ [property]: newValue });
@ -335,23 +331,21 @@ export class DS4ActorSheet extends ActorSheet {
*/ */
parseValue(element) { parseValue(element) {
switch (element.type) { switch (element.type) {
case "checkbox": { case 'checkbox': {
const inverted = Boolean(element.dataset["inverted"]); const inverted = Boolean(element.dataset['inverted']);
const value = element.checked; const value = element.checked;
return inverted ? !value : value; return inverted ? !value : value;
} }
case "text": { case 'text': {
const value = element.value; const value = element.value;
return value; return value;
} }
case "number": { case 'number': {
const value = Number(element.value.trim()); const value = Number(element.value.trim());
return value; return value;
} }
default: { default: {
throw new TypeError( throw new TypeError('Binding of item property to this type of HTML element not supported; given: ' + element);
"Binding of item property to this type of HTML element not supported; given: " + element,
);
} }
} }
} }
@ -368,7 +362,7 @@ export class DS4ActorSheet extends ActorSheet {
const item = await fromUuid(uuid); const item = await fromUuid(uuid);
enforce( enforce(
item && item.parent === this.actor, item && item.parent === this.actor,
getGame().i18n.format("DS4.ErrorActorDoesNotHaveItem", { uuid, actor: this.actor.name }), getGame().i18n.format('DS4.ErrorActorDoesNotHaveItem', { uuid, actor: this.actor.name }),
); );
item.roll().catch((e) => notifications.error(e, { log: true })); item.roll().catch((e) => notifications.error(e, { log: true }));
} }
@ -381,7 +375,7 @@ export class DS4ActorSheet extends ActorSheet {
onRollCheck(event) { onRollCheck(event) {
event.preventDefault(); event.preventDefault();
event.currentTarget.blur(); event.currentTarget.blur();
const check = event.currentTarget.dataset["check"]; const check = event.currentTarget.dataset['check'];
this.actor.rollCheck(check).catch((e) => notifications.error(e, { log: true })); this.actor.rollCheck(check).catch((e) => notifications.error(e, { log: true }));
} }
@ -396,17 +390,17 @@ export class DS4ActorSheet extends ActorSheet {
const check = target.dataset.check; const check = target.dataset.check;
if (!check) return super._onDragStart(event); if (!check) return super._onDragStart(event);
enforce(isCheck(check), getGame().i18n.format("DS4.ErrorCannotDragMissingCheck", { check })); enforce(isCheck(check), getGame().i18n.format('DS4.ErrorCannotDragMissingCheck', { check }));
const dragData = { const dragData = {
actorId: this.actor.id, actorId: this.actor.id,
sceneId: this.actor.isToken ? getCanvas().scene?.id : null, sceneId: this.actor.isToken ? getCanvas().scene?.id : null,
tokenId: this.actor.isToken ? this.actor.token?.id : null, tokenId: this.actor.isToken ? this.actor.token?.id : null,
type: "Check", type: 'Check',
data: check, data: check,
}; };
event.dataTransfer?.setData("text/plain", JSON.stringify(dragData)); event.dataTransfer?.setData('text/plain', JSON.stringify(dragData));
} }
/** /**
@ -417,11 +411,11 @@ export class DS4ActorSheet extends ActorSheet {
onSortItems(event) { onSortItems(event) {
event.preventDefault(); event.preventDefault();
const target = event.currentTarget; const target = event.currentTarget;
const type = target.parentElement?.dataset["type"]; const type = target.parentElement?.dataset['type'];
enforce(type !== undefined, `Could not find property 'type' in the dataset of the parent of ${target}`); enforce(type !== undefined, `Could not find property 'type' in the dataset of the parent of ${target}`);
const dataPath = target.dataset["dataPath"]; const dataPath = target.dataset['dataPath'];
enforce(dataPath !== undefined, `Could not find property 'dataPath' in the dataset of ${target}`); enforce(dataPath !== undefined, `Could not find property 'dataPath' in the dataset of ${target}`);
const dataPath2 = target.dataset["dataPath2"]; const dataPath2 = target.dataset['dataPath2'];
/** @type {import("../../documents/item/item").DS4Item[]}*/ /** @type {import("../../documents/item/item").DS4Item[]}*/
const items = this.actor.items.filter((item) => item.type === type); const items = this.actor.items.filter((item) => item.type === type);
items.sort((a, b) => a.sort - b.sort); items.sort((a, b) => a.sort - b.sort);
@ -434,14 +428,14 @@ export class DS4ActorSheet extends ActorSheet {
const propertyA = getProperty(a, dataPath); const propertyA = getProperty(a, dataPath);
const propertyB = getProperty(b, dataPath); const propertyB = getProperty(b, dataPath);
const comparison = const comparison =
typeof propertyA === "string" || typeof propertyB === "string" typeof propertyA === 'string' || typeof propertyB === 'string'
? compareAsStrings(propertyA, propertyB, invert) ? compareAsStrings(propertyA, propertyB, invert)
: compareAsNumbers(propertyA, propertyB, invert); : compareAsNumbers(propertyA, propertyB, invert);
if (comparison === 0 && dataPath2 !== undefined) { if (comparison === 0 && dataPath2 !== undefined) {
const propertyA = getProperty(a, dataPath); const propertyA = getProperty(a, dataPath);
const propertyB = getProperty(b, dataPath); const propertyB = getProperty(b, dataPath);
return typeof propertyA === "string" || typeof propertyB === "string" return typeof propertyA === 'string' || typeof propertyB === 'string'
? compareAsStrings(propertyA, propertyB, invert) ? compareAsStrings(propertyA, propertyB, invert)
: compareAsNumbers(propertyA, propertyB, invert); : compareAsNumbers(propertyA, propertyB, invert);
} }
@ -461,7 +455,7 @@ export class DS4ActorSheet extends ActorSheet {
sort: (i + 1) * CONST.SORT_INTEGER_DENSITY, sort: (i + 1) * CONST.SORT_INTEGER_DENSITY,
})); }));
this.actor.updateEmbeddedDocuments("Item", updates); this.actor.updateEmbeddedDocuments('Item', updates);
} }
/** /**
@ -473,7 +467,7 @@ export class DS4ActorSheet extends ActorSheet {
const item = await Item.implementation.fromDropData(data); const item = await Item.implementation.fromDropData(data);
if (item && !this.actor.canOwnItemType(item.type)) { if (item && !this.actor.canOwnItemType(item.type)) {
notifications.warn( notifications.warn(
getGame().i18n.format("DS4.WarningActorCannotOwnItem", { getGame().i18n.format('DS4.WarningActorCannotOwnItem', {
actorName: this.actor.name, actorName: this.actor.name,
actorType: this.actor.type, actorType: this.actor.type,
itemName: item.name, itemName: item.name,
@ -491,12 +485,12 @@ export class DS4ActorSheet extends ActorSheet {
*/ */
const embeddedDocumentListEntryProperties = Object.freeze({ const embeddedDocumentListEntryProperties = Object.freeze({
ActiveEffect: { ActiveEffect: {
selector: ".effect", selector: '.effect',
uuidDataAttribute: "effectUuid", uuidDataAttribute: 'effectUuid',
}, },
Item: { Item: {
selector: ".item", selector: '.item',
uuidDataAttribute: "itemUuid", uuidDataAttribute: 'itemUuid',
}, },
}); });

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4ActorSheet } from "./base-sheet"; import { DS4ActorSheet } from './base-sheet';
/** /**
* The Sheet class for DS4 Character Actors * The Sheet class for DS4 Character Actors
@ -10,7 +10,7 @@ import { DS4ActorSheet } from "./base-sheet";
export class DS4CharacterActorSheet extends DS4ActorSheet { export class DS4CharacterActorSheet extends DS4ActorSheet {
static get defaultOptions() { static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["sheet", "ds4-actor-sheet", "ds4-character-sheet"], classes: ['sheet', 'ds4-actor-sheet', 'ds4-character-sheet'],
}); });
} }

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4ActorSheet } from "./base-sheet"; import { DS4ActorSheet } from './base-sheet';
/** /**
* The Sheet class for DS4 Creature Actors * The Sheet class for DS4 Creature Actors
@ -10,17 +10,16 @@ import { DS4ActorSheet } from "./base-sheet";
export class DS4CreatureActorSheet extends DS4ActorSheet { export class DS4CreatureActorSheet extends DS4ActorSheet {
static get defaultOptions() { static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["sheet", "ds4-actor-sheet", "ds4-creature-sheet"], classes: ['sheet', 'ds4-actor-sheet', 'ds4-creature-sheet'],
}); });
} }
/** @override */ /** @override */
async getData(options = {}) { async getData(options = {}) {
const context = await super.getData(options); const context = await super.getData(options);
context.data.system.baseInfo.description = await TextEditor.enrichHTML( context.data.system.baseInfo.description = await TextEditor.enrichHTML(context.data.system.baseInfo.description, {
context.data.system.baseInfo.description, async: true,
{ async: true }, });
);
return context; return context;
} }
} }

View file

@ -4,10 +4,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4 } from "../config"; import { DS4 } from '../config';
import { DS4ActiveEffect } from "../documents/active-effect"; import { DS4ActiveEffect } from '../documents/active-effect';
import { enforce, getGame } from "../utils/utils"; import { enforce, getGame } from '../utils/utils';
import { disableOverriddenFields } from "./sheet-helpers"; import { disableOverriddenFields } from './sheet-helpers';
/** /**
* The Sheet class for DS4 Items * The Sheet class for DS4 Items
@ -16,17 +16,17 @@ export class DS4ItemSheet extends ItemSheet {
/** @override */ /** @override */
static get defaultOptions() { static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, { return foundry.utils.mergeObject(super.defaultOptions, {
classes: ["sheet", "ds4-item-sheet"], classes: ['sheet', 'ds4-item-sheet'],
height: 400, height: 400,
scrollY: [".ds4-sheet-body"], scrollY: ['.ds4-sheet-body'],
tabs: [{ navSelector: ".ds4-sheet-tab-nav", contentSelector: ".ds4-sheet-body", initial: "description" }], tabs: [{ navSelector: '.ds4-sheet-tab-nav', contentSelector: '.ds4-sheet-body', initial: 'description' }],
width: 540, width: 540,
}); });
} }
/** @override */ /** @override */
get template() { get template() {
const basePath = "systems/ds4/templates/sheets/item"; const basePath = 'systems/ds4/templates/sheets/item';
return `${basePath}/${this.item.type}-sheet.hbs`; return `${basePath}/${this.item.type}-sheet.hbs`;
} }
@ -60,9 +60,9 @@ export class DS4ItemSheet extends ItemSheet {
setPosition(options = {}) { setPosition(options = {}) {
const position = super.setPosition(options); const position = super.setPosition(options);
if (position) { if (position) {
const sheetBody = this.element.find(".sheet-body"); const sheetBody = this.element.find('.sheet-body');
const bodyHeight = position.height - 192; const bodyHeight = position.height - 192;
sheetBody.css("height", bodyHeight); sheetBody.css('height', bodyHeight);
} }
return position; return position;
@ -77,7 +77,7 @@ export class DS4ItemSheet extends ItemSheet {
if (!this.options.editable) return; if (!this.options.editable) return;
html.find(".control-effect").on("click", this.onControlEffect.bind(this)); html.find('.control-effect').on('click', this.onControlEffect.bind(this));
disableOverriddenFields(this.form, this.item.overrides, (key) => `[name="${key}"]`); disableOverriddenFields(this.form, this.item.overrides, (key) => `[name="${key}"]`);
} }
@ -92,12 +92,12 @@ export class DS4ItemSheet extends ItemSheet {
onControlEffect(event) { onControlEffect(event) {
event.preventDefault(); event.preventDefault();
const a = event.currentTarget; const a = event.currentTarget;
switch (a.dataset["action"]) { switch (a.dataset['action']) {
case "create": case 'create':
return this.onCreateEffect(); return this.onCreateEffect();
case "edit": case 'edit':
return this.onEditEffect(event); return this.onEditEffect(event);
case "delete": case 'delete':
return this.onDeleteEffect(event); return this.onDeleteEffect(event);
} }
} }
@ -121,7 +121,7 @@ export class DS4ItemSheet extends ItemSheet {
.parents(embeddedDocumentListEntryProperties.ActiveEffect.selector) .parents(embeddedDocumentListEntryProperties.ActiveEffect.selector)
.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute); .data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
const effect = this.item.effects.get(id); const effect = this.item.effects.get(id);
enforce(effect, getGame().i18n.format("DS4.ErrorItemDoesNotHaveEffect", { id, item: this.item.name })); enforce(effect, getGame().i18n.format('DS4.ErrorItemDoesNotHaveEffect', { id, item: this.item.name }));
effect.sheet?.render(true); effect.sheet?.render(true);
} }
@ -134,7 +134,7 @@ export class DS4ItemSheet extends ItemSheet {
onDeleteEffect(event) { onDeleteEffect(event) {
const li = $(event.currentTarget).parents(embeddedDocumentListEntryProperties.ActiveEffect.selector); const li = $(event.currentTarget).parents(embeddedDocumentListEntryProperties.ActiveEffect.selector);
const id = li.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute); const id = li.data(embeddedDocumentListEntryProperties.ActiveEffect.idDataAttribute);
this.item.deleteEmbeddedDocuments("ActiveEffect", [id]); this.item.deleteEmbeddedDocuments('ActiveEffect', [id]);
li.slideUp(200, () => this.render(false)); li.slideUp(200, () => this.render(false));
} }
} }
@ -144,7 +144,7 @@ export class DS4ItemSheet extends ItemSheet {
*/ */
const embeddedDocumentListEntryProperties = Object.freeze({ const embeddedDocumentListEntryProperties = Object.freeze({
ActiveEffect: { ActiveEffect: {
selector: ".effect", selector: '.effect',
idDataAttribute: "effectId", idDataAttribute: 'effectId',
}, },
}); });

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
/** /**
* Disable elements in the given form that match the selector returned for overridden properties. * Disable elements in the given form that match the selector returned for overridden properties.
@ -11,17 +11,17 @@ import { getGame } from "../utils/utils";
* @param {(key: string) => string} selector A function that generates a selector, based on a property key * @param {(key: string) => string} selector A function that generates a selector, based on a property key
*/ */
export function disableOverriddenFields(form, overrides, selector) { export function disableOverriddenFields(form, overrides, selector) {
const inputs = ["INPUT", "SELECT", "TEXTAREA", "BUTTON"]; const inputs = ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'];
const titleAddition = `(${getGame().i18n.localize("DS4.TooltipNotEditableDueToEffects")})`; const titleAddition = `(${getGame().i18n.localize('DS4.TooltipNotEditableDueToEffects')})`;
for (const key of Object.keys(foundry.utils.flattenObject(overrides))) { for (const key of Object.keys(foundry.utils.flattenObject(overrides))) {
const elements = form?.querySelectorAll(selector(key)); const elements = form?.querySelectorAll(selector(key));
elements?.forEach((element) => { elements?.forEach((element) => {
if (inputs.includes(element.tagName) && !element.hasAttribute("disabled")) { if (inputs.includes(element.tagName) && !element.hasAttribute('disabled')) {
element.setAttribute("disabled", ""); element.setAttribute('disabled', '');
const title = element.getAttribute("title"); const title = element.getAttribute('title');
const newTitle = title === null ? titleAddition : `${title} ${titleAddition}`; const newTitle = title === null ? titleAddition : `${title} ${titleAddition}`;
element.setAttribute("title", newTitle); element.setAttribute('title', newTitle);
} }
}); });
} }

View file

@ -10,348 +10,348 @@ const i18nKeys = {
* Define the set of acttack types that can be performed with weapon items * Define the set of acttack types that can be performed with weapon items
*/ */
attackTypes: { attackTypes: {
melee: "DS4.AttackTypeMelee", melee: 'DS4.AttackTypeMelee',
ranged: "DS4.AttackTypeRanged", ranged: 'DS4.AttackTypeRanged',
meleeRanged: "DS4.AttackTypeMeleeRanged", meleeRanged: 'DS4.AttackTypeMeleeRanged',
}, },
/** /**
* Define the set of item availabilties * Define the set of item availabilties
*/ */
itemAvailabilities: { itemAvailabilities: {
unset: "DS4.ItemAvailabilityUnset", unset: 'DS4.ItemAvailabilityUnset',
hamlet: "DS4.ItemAvailabilityHamlet", hamlet: 'DS4.ItemAvailabilityHamlet',
village: "DS4.ItemAvailabilityVilage", village: 'DS4.ItemAvailabilityVilage',
city: "DS4.ItemAvailabilityCity", city: 'DS4.ItemAvailabilityCity',
elves: "DS4.ItemAvailabilityElves", elves: 'DS4.ItemAvailabilityElves',
dwarves: "DS4.ItemAvailabilityDwarves", dwarves: 'DS4.ItemAvailabilityDwarves',
nowhere: "DS4.ItemAvailabilityNowhere", nowhere: 'DS4.ItemAvailabilityNowhere',
}, },
/** /**
* Define the set of item types * Define the set of item types
*/ */
itemTypes: { itemTypes: {
weapon: "DS4.ItemTypeWeapon", weapon: 'DS4.ItemTypeWeapon',
armor: "DS4.ItemTypeArmor", armor: 'DS4.ItemTypeArmor',
shield: "DS4.ItemTypeShield", shield: 'DS4.ItemTypeShield',
spell: "DS4.ItemTypeSpell", spell: 'DS4.ItemTypeSpell',
equipment: "DS4.ItemTypeEquipment", equipment: 'DS4.ItemTypeEquipment',
loot: "DS4.ItemTypeLoot", loot: 'DS4.ItemTypeLoot',
talent: "DS4.ItemTypeTalent", talent: 'DS4.ItemTypeTalent',
racialAbility: "DS4.ItemTypeRacialAbility", racialAbility: 'DS4.ItemTypeRacialAbility',
language: "DS4.ItemTypeLanguage", language: 'DS4.ItemTypeLanguage',
alphabet: "DS4.ItemTypeAlphabet", alphabet: 'DS4.ItemTypeAlphabet',
specialCreatureAbility: "DS4.ItemTypeSpecialCreatureAbility", specialCreatureAbility: 'DS4.ItemTypeSpecialCreatureAbility',
}, },
/** /**
* Define the set of armor types, a character may only wear one item of each at any given time * Define the set of armor types, a character may only wear one item of each at any given time
*/ */
armorTypes: { armorTypes: {
body: "DS4.ArmorTypeBody", body: 'DS4.ArmorTypeBody',
helmet: "DS4.ArmorTypeHelmet", helmet: 'DS4.ArmorTypeHelmet',
vambrace: "DS4.ArmorTypeVambrace", vambrace: 'DS4.ArmorTypeVambrace',
greaves: "DS4.ArmorTypeGreaves", greaves: 'DS4.ArmorTypeGreaves',
vambraceGreaves: "DS4.ArmorTypeVambraceGreaves", vambraceGreaves: 'DS4.ArmorTypeVambraceGreaves',
}, },
/** /**
* Define abbreviations for the armor types * Define abbreviations for the armor types
*/ */
armorTypesAbbr: { armorTypesAbbr: {
body: "DS4.ArmorTypeBodyAbbr", body: 'DS4.ArmorTypeBodyAbbr',
helmet: "DS4.ArmorTypeHelmetAbbr", helmet: 'DS4.ArmorTypeHelmetAbbr',
vambrace: "DS4.ArmorTypeVambraceAbbr", vambrace: 'DS4.ArmorTypeVambraceAbbr',
greaves: "DS4.ArmorTypeGreavesAbbr", greaves: 'DS4.ArmorTypeGreavesAbbr',
vambraceGreaves: "DS4.ArmorTypeVambraceGreavesAbbr", vambraceGreaves: 'DS4.ArmorTypeVambraceGreavesAbbr',
}, },
/** /**
* Define the set of armor materials, used to determine if a character may wear the armor without additional penalties * Define the set of armor materials, used to determine if a character may wear the armor without additional penalties
*/ */
armorMaterialTypes: { armorMaterialTypes: {
cloth: "DS4.ArmorMaterialTypeCloth", cloth: 'DS4.ArmorMaterialTypeCloth',
leather: "DS4.ArmorMaterialTypeLeather", leather: 'DS4.ArmorMaterialTypeLeather',
chain: "DS4.ArmorMaterialTypeChain", chain: 'DS4.ArmorMaterialTypeChain',
plate: "DS4.ArmorMaterialTypePlate", plate: 'DS4.ArmorMaterialTypePlate',
natural: "DS4.ArmorMaterialTypeNatural", natural: 'DS4.ArmorMaterialTypeNatural',
}, },
/** /**
* Define the abbreviations of armor materials * Define the abbreviations of armor materials
*/ */
armorMaterialTypesAbbr: { armorMaterialTypesAbbr: {
cloth: "DS4.ArmorMaterialTypeClothAbbr", cloth: 'DS4.ArmorMaterialTypeClothAbbr',
leather: "DS4.ArmorMaterialTypeLeatherAbbr", leather: 'DS4.ArmorMaterialTypeLeatherAbbr',
chain: "DS4.ArmorMaterialTypeChainAbbr", chain: 'DS4.ArmorMaterialTypeChainAbbr',
plate: "DS4.ArmorMaterialTypePlateAbbr", plate: 'DS4.ArmorMaterialTypePlateAbbr',
natural: "DS4.ArmorMaterialTypeNaturalAbbr", natural: 'DS4.ArmorMaterialTypeNaturalAbbr',
}, },
spellTypes: { spellTypes: {
spellcasting: "DS4.SpellTypeSpellcasting", spellcasting: 'DS4.SpellTypeSpellcasting',
targetedSpellcasting: "DS4.SpellTypeTargetedSpellcasting", targetedSpellcasting: 'DS4.SpellTypeTargetedSpellcasting',
}, },
spellGroups: { spellGroups: {
lightning: "DS4.SpellGroupLightning", lightning: 'DS4.SpellGroupLightning',
earth: "DS4.SpellGroupEarth", earth: 'DS4.SpellGroupEarth',
water: "DS4.SpellGroupWater", water: 'DS4.SpellGroupWater',
ice: "DS4.SpellGroupIce", ice: 'DS4.SpellGroupIce',
fire: "DS4.SpellGroupFire", fire: 'DS4.SpellGroupFire',
healing: "DS4.SpellGroupHealing", healing: 'DS4.SpellGroupHealing',
light: "DS4.SpellGroupLight", light: 'DS4.SpellGroupLight',
air: "DS4.SpellGroupAir", air: 'DS4.SpellGroupAir',
transport: "DS4.SpellGroupTransport", transport: 'DS4.SpellGroupTransport',
damage: "DS4.SpellGroupDamage", damage: 'DS4.SpellGroupDamage',
shadow: "DS4.SpellGroupShadow", shadow: 'DS4.SpellGroupShadow',
protection: "DS4.SpellGroupProtection", protection: 'DS4.SpellGroupProtection',
mindAffecting: "DS4.SpellGroupMindAffecting", mindAffecting: 'DS4.SpellGroupMindAffecting',
demonology: "DS4.SpellGroupDemonology", demonology: 'DS4.SpellGroupDemonology',
necromancy: "DS4.SpellGroupNecromancy", necromancy: 'DS4.SpellGroupNecromancy',
transmutation: "DS4.SpellGroupTransmutation", transmutation: 'DS4.SpellGroupTransmutation',
area: "DS4.SpellGroupArea", area: 'DS4.SpellGroupArea',
}, },
cooldownDurations: { cooldownDurations: {
"0r": "DS4.CooldownDuration0R", '0r': 'DS4.CooldownDuration0R',
"1r": "DS4.CooldownDuration1R", '1r': 'DS4.CooldownDuration1R',
"2r": "DS4.CooldownDuration2R", '2r': 'DS4.CooldownDuration2R',
"5r": "DS4.CooldownDuration5R", '5r': 'DS4.CooldownDuration5R',
"10r": "DS4.CooldownDuration10R", '10r': 'DS4.CooldownDuration10R',
"100r": "DS4.CooldownDuration100R", '100r': 'DS4.CooldownDuration100R',
"1d": "DS4.CooldownDuration1D", '1d': 'DS4.CooldownDuration1D',
d20d: "DS4.CooldownDurationD20D", d20d: 'DS4.CooldownDurationD20D',
}, },
/** /**
* Define the set of actor types * Define the set of actor types
*/ */
actorTypes: { actorTypes: {
character: "DS4.ActorTypeCharacter", character: 'DS4.ActorTypeCharacter',
creature: "DS4.ActorTypeCreature", creature: 'DS4.ActorTypeCreature',
}, },
/** /**
* Define the set of attributes an actor has * Define the set of attributes an actor has
*/ */
attributes: { attributes: {
body: "DS4.AttributeBody", body: 'DS4.AttributeBody',
mobility: "DS4.AttributeMobility", mobility: 'DS4.AttributeMobility',
mind: "DS4.AttributeMind", mind: 'DS4.AttributeMind',
}, },
/** /**
* Define the set of traits an actor has * Define the set of traits an actor has
*/ */
traits: { traits: {
strength: "DS4.TraitStrength", strength: 'DS4.TraitStrength',
agility: "DS4.TraitAgility", agility: 'DS4.TraitAgility',
intellect: "DS4.TraitIntellect", intellect: 'DS4.TraitIntellect',
constitution: "DS4.TraitConstitution", constitution: 'DS4.TraitConstitution',
dexterity: "DS4.TraitDexterity", dexterity: 'DS4.TraitDexterity',
aura: "DS4.TraitAura", aura: 'DS4.TraitAura',
}, },
/** /**
* Define the set of combat values an actor has * Define the set of combat values an actor has
*/ */
combatValues: { combatValues: {
hitPoints: "DS4.CombatValuesHitPoints", hitPoints: 'DS4.CombatValuesHitPoints',
defense: "DS4.CombatValuesDefense", defense: 'DS4.CombatValuesDefense',
initiative: "DS4.CombatValuesInitiative", initiative: 'DS4.CombatValuesInitiative',
movement: "DS4.CombatValuesMovement", movement: 'DS4.CombatValuesMovement',
meleeAttack: "DS4.CombatValuesMeleeAttack", meleeAttack: 'DS4.CombatValuesMeleeAttack',
rangedAttack: "DS4.CombatValuesRangedAttack", rangedAttack: 'DS4.CombatValuesRangedAttack',
spellcasting: "DS4.CombatValuesSpellcasting", spellcasting: 'DS4.CombatValuesSpellcasting',
targetedSpellcasting: "DS4.CombatValuesTargetedSpellcasting", targetedSpellcasting: 'DS4.CombatValuesTargetedSpellcasting',
}, },
/** /**
* The what do display in the actor sheets for the combat value text (in some languages, abbreviations are necessary) * The what do display in the actor sheets for the combat value text (in some languages, abbreviations are necessary)
*/ */
combatValuesSheet: { combatValuesSheet: {
hitPoints: "DS4.CombatValuesHitPointsSheet", hitPoints: 'DS4.CombatValuesHitPointsSheet',
defense: "DS4.CombatValuesDefenseSheet", defense: 'DS4.CombatValuesDefenseSheet',
initiative: "DS4.CombatValuesInitiativeSheet", initiative: 'DS4.CombatValuesInitiativeSheet',
movement: "DS4.CombatValuesMovementSheet", movement: 'DS4.CombatValuesMovementSheet',
meleeAttack: "DS4.CombatValuesMeleeAttackSheet", meleeAttack: 'DS4.CombatValuesMeleeAttackSheet',
rangedAttack: "DS4.CombatValuesRangedAttackSheet", rangedAttack: 'DS4.CombatValuesRangedAttackSheet',
spellcasting: "DS4.CombatValuesSpellcastingSheet", spellcasting: 'DS4.CombatValuesSpellcastingSheet',
targetedSpellcasting: "DS4.CombatValuesTargetedSpellcastingSheet", targetedSpellcasting: 'DS4.CombatValuesTargetedSpellcastingSheet',
}, },
/** /**
* Define the base info of a character * Define the base info of a character
*/ */
characterBaseInfo: { characterBaseInfo: {
race: "DS4.CharacterBaseInfoRace", race: 'DS4.CharacterBaseInfoRace',
class: "DS4.CharacterBaseInfoClass", class: 'DS4.CharacterBaseInfoClass',
heroClass: "DS4.CharacterBaseInfoHeroClass", heroClass: 'DS4.CharacterBaseInfoHeroClass',
culture: "DS4.CharacterBaseInfoCulture", culture: 'DS4.CharacterBaseInfoCulture',
}, },
/** /**
* Define the progression info of a character * Define the progression info of a character
*/ */
characterProgression: { characterProgression: {
level: "DS4.CharacterProgressionLevel", level: 'DS4.CharacterProgressionLevel',
experiencePoints: "DS4.CharacterProgressionExperiencePoints", experiencePoints: 'DS4.CharacterProgressionExperiencePoints',
talentPoints: "DS4.CharacterProgressionTalentPoints", talentPoints: 'DS4.CharacterProgressionTalentPoints',
progressPoints: "DS4.CharacterProgressionProgressPoints", progressPoints: 'DS4.CharacterProgressionProgressPoints',
}, },
/** /**
* Define the language info of a character * Define the language info of a character
*/ */
characterLanguage: { characterLanguage: {
languages: "DS4.CharacterLanguageLanguages", languages: 'DS4.CharacterLanguageLanguages',
alphabets: "DS4.CharacterLanguageAlphabets", alphabets: 'DS4.CharacterLanguageAlphabets',
}, },
/** /**
* Define the profile info of a character * Define the profile info of a character
*/ */
characterProfile: { characterProfile: {
biography: "DS4.CharacterProfileBiography", biography: 'DS4.CharacterProfileBiography',
gender: "DS4.CharacterProfileGender", gender: 'DS4.CharacterProfileGender',
birthday: "DS4.CharacterProfileBirthday", birthday: 'DS4.CharacterProfileBirthday',
birthplace: "DS4.CharacterProfileBirthplace", birthplace: 'DS4.CharacterProfileBirthplace',
age: "DS4.CharacterProfileAge", age: 'DS4.CharacterProfileAge',
height: "DS4.CharacterProfileHeight", height: 'DS4.CharacterProfileHeight',
hairColor: "DS4.CharacterProfileHairColor", hairColor: 'DS4.CharacterProfileHairColor',
weight: "DS4.CharacterProfileWeight", weight: 'DS4.CharacterProfileWeight',
eyeColor: "DS4.CharacterProfileEyeColor", eyeColor: 'DS4.CharacterProfileEyeColor',
specialCharacteristics: "DS4.CharacterProfileSpecialCharacteristics", specialCharacteristics: 'DS4.CharacterProfileSpecialCharacteristics',
}, },
/** /**
* Define currency elements of a character * Define currency elements of a character
*/ */
characterCurrency: { characterCurrency: {
gold: "DS4.CharacterCurrencyGold", gold: 'DS4.CharacterCurrencyGold',
silver: "DS4.CharacterCurrencySilver", silver: 'DS4.CharacterCurrencySilver',
copper: "DS4.CharacterCurrencyCopper", copper: 'DS4.CharacterCurrencyCopper',
}, },
/** /**
* Define the different creature types a creature can be * Define the different creature types a creature can be
*/ */
creatureTypes: { creatureTypes: {
animal: "DS4.CreatureTypeAnimal", animal: 'DS4.CreatureTypeAnimal',
construct: "DS4.CreatureTypeConstruct", construct: 'DS4.CreatureTypeConstruct',
humanoid: "DS4.CreatureTypeHumanoid", humanoid: 'DS4.CreatureTypeHumanoid',
magicalEntity: "DS4.CreatureTypeMagicalEntity", magicalEntity: 'DS4.CreatureTypeMagicalEntity',
plantBeing: "DS4.CreatureTypePlantBeing", plantBeing: 'DS4.CreatureTypePlantBeing',
undead: "DS4.CreatureTypeUndead", undead: 'DS4.CreatureTypeUndead',
}, },
/** /**
* Define the different size categories creatures fall into * Define the different size categories creatures fall into
*/ */
creatureSizeCategories: { creatureSizeCategories: {
tiny: "DS4.CreatureSizeCategoryTiny", tiny: 'DS4.CreatureSizeCategoryTiny',
small: "DS4.CreatureSizeCategorySmall", small: 'DS4.CreatureSizeCategorySmall',
normal: "DS4.CreatureSizeCategoryNormal", normal: 'DS4.CreatureSizeCategoryNormal',
large: "DS4.CreatureSizeCategoryLarge", large: 'DS4.CreatureSizeCategoryLarge',
huge: "DS4.CreatureSizeCategoryHuge", huge: 'DS4.CreatureSizeCategoryHuge',
colossal: "DS4.CreatureSizeCategoryColossal", colossal: 'DS4.CreatureSizeCategoryColossal',
}, },
/** /**
* Define the base info of a creature * Define the base info of a creature
*/ */
creatureBaseInfo: { creatureBaseInfo: {
loot: "DS4.CreatureBaseInfoLoot", loot: 'DS4.CreatureBaseInfoLoot',
foeFactor: "DS4.CreatureBaseInfoFoeFactor", foeFactor: 'DS4.CreatureBaseInfoFoeFactor',
creatureType: "DS4.CreatureBaseInfoCreatureType", creatureType: 'DS4.CreatureBaseInfoCreatureType',
sizeCategory: "DS4.CreatureBaseInfoSizeCategory", sizeCategory: 'DS4.CreatureBaseInfoSizeCategory',
experiencePoints: "DS4.CreatureBaseInfoExperiencePoints", experiencePoints: 'DS4.CreatureBaseInfoExperiencePoints',
description: "DS4.CreatureBaseInfoDescription", description: 'DS4.CreatureBaseInfoDescription',
}, },
/** /**
* Define translations for available distance units * Define translations for available distance units
*/ */
distanceUnits: { distanceUnits: {
meter: "DS4.UnitMeters", meter: 'DS4.UnitMeters',
kilometer: "DS4.UnitKilometers", kilometer: 'DS4.UnitKilometers',
custom: "DS4.UnitCustom", custom: 'DS4.UnitCustom',
}, },
/** /**
* Define abbreviations for available distance units * Define abbreviations for available distance units
*/ */
distanceUnitsAbbr: { distanceUnitsAbbr: {
meter: "DS4.UnitMetersAbbr", meter: 'DS4.UnitMetersAbbr',
kilometer: "DS4.UnitKilometersAbbr", kilometer: 'DS4.UnitKilometersAbbr',
custom: "DS4.UnitCustomAbbr", custom: 'DS4.UnitCustomAbbr',
}, },
/** /**
* Define translations for available duration units * Define translations for available duration units
*/ */
temporalUnits: { temporalUnits: {
rounds: "DS4.UnitRounds", rounds: 'DS4.UnitRounds',
minutes: "DS4.UnitMinutes", minutes: 'DS4.UnitMinutes',
hours: "DS4.UnitHours", hours: 'DS4.UnitHours',
days: "DS4.UnitDays", days: 'DS4.UnitDays',
custom: "DS4.UnitCustom", custom: 'DS4.UnitCustom',
}, },
/** /**
* Define abbreviations for available duration units * Define abbreviations for available duration units
*/ */
temporalUnitsAbbr: { temporalUnitsAbbr: {
rounds: "DS4.UnitRoundsAbbr", rounds: 'DS4.UnitRoundsAbbr',
minutes: "DS4.UnitMinutesAbbr", minutes: 'DS4.UnitMinutesAbbr',
hours: "DS4.UnitHoursAbbr", hours: 'DS4.UnitHoursAbbr',
days: "DS4.UnitDaysAbbr", days: 'DS4.UnitDaysAbbr',
custom: "DS4.UnitCustomAbbr", custom: 'DS4.UnitCustomAbbr',
}, },
checks: { checks: {
appraise: "DS4.ChecksAppraise", appraise: 'DS4.ChecksAppraise',
changeSpell: "DS4.ChecksChangeSpell", changeSpell: 'DS4.ChecksChangeSpell',
climb: "DS4.ChecksClimb", climb: 'DS4.ChecksClimb',
communicate: "DS4.ChecksCommunicate", communicate: 'DS4.ChecksCommunicate',
decipherScript: "DS4.ChecksDecipherScript", decipherScript: 'DS4.ChecksDecipherScript',
defend: "DS4.ChecksDefend", defend: 'DS4.ChecksDefend',
defyPoison: "DS4.ChecksDefyPoison", defyPoison: 'DS4.ChecksDefyPoison',
disableTraps: "DS4.ChecksDisableTraps", disableTraps: 'DS4.ChecksDisableTraps',
featOfStrength: "DS4.ChecksFeatOfStrength", featOfStrength: 'DS4.ChecksFeatOfStrength',
flirt: "DS4.ChecksFlirt", flirt: 'DS4.ChecksFlirt',
haggle: "DS4.ChecksHaggle", haggle: 'DS4.ChecksHaggle',
hide: "DS4.ChecksHide", hide: 'DS4.ChecksHide',
identifyMagic: "DS4.ChecksIdentifyMagic", identifyMagic: 'DS4.ChecksIdentifyMagic',
jump: "DS4.ChecksJump", jump: 'DS4.ChecksJump',
knowledge: "DS4.ChecksKnowledge", knowledge: 'DS4.ChecksKnowledge',
openLock: "DS4.ChecksOpenLock", openLock: 'DS4.ChecksOpenLock',
perception: "DS4.ChecksPerception", perception: 'DS4.ChecksPerception',
pickPocket: "DS4.ChecksPickPocket", pickPocket: 'DS4.ChecksPickPocket',
readTracks: "DS4.ChecksReadTracks", readTracks: 'DS4.ChecksReadTracks',
resistDisease: "DS4.ChecksResistDisease", resistDisease: 'DS4.ChecksResistDisease',
ride: "DS4.ChecksRide", ride: 'DS4.ChecksRide',
search: "DS4.ChecksSearch", search: 'DS4.ChecksSearch',
senseMagic: "DS4.ChecksSenseMagic", senseMagic: 'DS4.ChecksSenseMagic',
sneak: "DS4.ChecksSneak", sneak: 'DS4.ChecksSneak',
startFire: "DS4.ChecksStartFire", startFire: 'DS4.ChecksStartFire',
swim: "DS4.ChecksSwim", swim: 'DS4.ChecksSwim',
wakeUp: "DS4.ChecksWakeUp", wakeUp: 'DS4.ChecksWakeUp',
workMechanism: "DS4.ChecksWorkMechanism", workMechanism: 'DS4.ChecksWorkMechanism',
}, },
/** /**
* Translations for the standard check modifiers * Translations for the standard check modifiers
*/ */
checkModifiers: { checkModifiers: {
routine: "DS4.CheckModifierRoutine", routine: 'DS4.CheckModifierRoutine',
veryEasy: "DS4.CheckModifierVeryEasy", veryEasy: 'DS4.CheckModifierVeryEasy',
easy: "DS4.CheckModifierEasy", easy: 'DS4.CheckModifierEasy',
normal: "DS4.CheckModifierMormal", normal: 'DS4.CheckModifierMormal',
difficult: "DS4.CheckModifierDifficult", difficult: 'DS4.CheckModifierDifficult',
veryDifficult: "DS4.CheckModifierVeryDifficult", veryDifficult: 'DS4.CheckModifierVeryDifficult',
extremelyDifficult: "DS4.CheckModifierExtremelyDifficult", extremelyDifficult: 'DS4.CheckModifierExtremelyDifficult',
custom: "DS4.CheckModifierCustom", custom: 'DS4.CheckModifierCustom',
}, },
}; };
@ -385,51 +385,51 @@ export const DS4 = {
* Define the file paths to icon images * Define the file paths to icon images
*/ */
attackTypes: { attackTypes: {
melee: "systems/ds4/assets/icons/official/combat-values/melee-attack.png", melee: 'systems/ds4/assets/icons/official/combat-values/melee-attack.png',
meleeRanged: "systems/ds4/assets/icons/official/combat-values/melee-ranged-attack.png", meleeRanged: 'systems/ds4/assets/icons/official/combat-values/melee-ranged-attack.png',
ranged: "systems/ds4/assets/icons/official/combat-values/ranged-attack.png", ranged: 'systems/ds4/assets/icons/official/combat-values/ranged-attack.png',
}, },
/** /**
* Define the file paths to icon images * Define the file paths to icon images
*/ */
spellTypes: { spellTypes: {
spellcasting: "systems/ds4/assets/icons/official/combat-values/spellcasting.png", spellcasting: 'systems/ds4/assets/icons/official/combat-values/spellcasting.png',
targetedSpellcasting: "systems/ds4/assets/icons/official/combat-values/targeted-spellcasting.png", targetedSpellcasting: 'systems/ds4/assets/icons/official/combat-values/targeted-spellcasting.png',
}, },
/** /**
* Define the file paths to check images * Define the file paths to check images
*/ */
checks: { checks: {
appraise: "systems/ds4/assets/icons/game-icons/delapouite/two-coins.svg", appraise: 'systems/ds4/assets/icons/game-icons/delapouite/two-coins.svg',
changeSpell: "systems/ds4/assets/icons/game-icons/delapouite/card-exchange.svg", changeSpell: 'systems/ds4/assets/icons/game-icons/delapouite/card-exchange.svg',
climb: "systems/ds4/assets/icons/game-icons/caro-asercion/mountain-climbing.svg", climb: 'systems/ds4/assets/icons/game-icons/caro-asercion/mountain-climbing.svg',
communicate: "systems/ds4/assets/icons/game-icons/delapouite/discussion.svg", communicate: 'systems/ds4/assets/icons/game-icons/delapouite/discussion.svg',
decipherScript: "systems/ds4/assets/icons/game-icons/lorc/rune-stone.svg", decipherScript: 'systems/ds4/assets/icons/game-icons/lorc/rune-stone.svg',
defend: "systems/ds4/assets/icons/game-icons/sbed/shield.svg", defend: 'systems/ds4/assets/icons/game-icons/sbed/shield.svg',
defyPoison: "systems/ds4/assets/icons/game-icons/lorc/poison-bottle.svg", defyPoison: 'systems/ds4/assets/icons/game-icons/lorc/poison-bottle.svg',
disableTraps: "systems/ds4/assets/icons/game-icons/lorc/wolf-trap.svg", disableTraps: 'systems/ds4/assets/icons/game-icons/lorc/wolf-trap.svg',
featOfStrength: "systems/ds4/assets/icons/game-icons/delapouite/biceps.svg", featOfStrength: 'systems/ds4/assets/icons/game-icons/delapouite/biceps.svg',
flirt: "systems/ds4/assets/icons/game-icons/lorc/charm.svg", flirt: 'systems/ds4/assets/icons/game-icons/lorc/charm.svg',
haggle: "systems/ds4/assets/icons/game-icons/lorc/cash.svg", haggle: 'systems/ds4/assets/icons/game-icons/lorc/cash.svg',
hide: "systems/ds4/assets/icons/game-icons/lorc/hidden.svg", hide: 'systems/ds4/assets/icons/game-icons/lorc/hidden.svg',
identifyMagic: "systems/ds4/assets/icons/game-icons/lorc/uncertainty.svg", identifyMagic: 'systems/ds4/assets/icons/game-icons/lorc/uncertainty.svg',
jump: "systems/ds4/assets/icons/game-icons/delapouite/jump-across.svg", jump: 'systems/ds4/assets/icons/game-icons/delapouite/jump-across.svg',
knowledge: "systems/ds4/assets/icons/game-icons/delapouite/bookshelf.svg", knowledge: 'systems/ds4/assets/icons/game-icons/delapouite/bookshelf.svg',
openLock: "systems/ds4/assets/icons/game-icons/delapouite/padlock-open.svg", openLock: 'systems/ds4/assets/icons/game-icons/delapouite/padlock-open.svg',
perception: "systems/ds4/assets/icons/game-icons/lorc/awareness.svg", perception: 'systems/ds4/assets/icons/game-icons/lorc/awareness.svg',
pickPocket: "systems/ds4/assets/icons/game-icons/darkzaitev/robber-hand.svg", pickPocket: 'systems/ds4/assets/icons/game-icons/darkzaitev/robber-hand.svg',
readTracks: "systems/ds4/assets/icons/game-icons/delapouite/deer-track.svg", readTracks: 'systems/ds4/assets/icons/game-icons/delapouite/deer-track.svg',
resistDisease: "systems/ds4/assets/icons/game-icons/lorc/virus.svg", resistDisease: 'systems/ds4/assets/icons/game-icons/lorc/virus.svg',
ride: "systems/ds4/assets/icons/game-icons/delapouite/cavalry.svg", ride: 'systems/ds4/assets/icons/game-icons/delapouite/cavalry.svg',
search: "systems/ds4/assets/icons/game-icons/lorc/magnifying-glass.svg", search: 'systems/ds4/assets/icons/game-icons/lorc/magnifying-glass.svg',
senseMagic: "systems/ds4/assets/icons/game-icons/delapouite/sparkles.svg", senseMagic: 'systems/ds4/assets/icons/game-icons/delapouite/sparkles.svg',
sneak: "systems/ds4/assets/icons/game-icons/delapouite/mute.svg", sneak: 'systems/ds4/assets/icons/game-icons/delapouite/mute.svg',
startFire: "systems/ds4/assets/icons/game-icons/lorc/campfire.svg", startFire: 'systems/ds4/assets/icons/game-icons/lorc/campfire.svg',
swim: "systems/ds4/assets/icons/game-icons/delapouite/pool-dive.svg", swim: 'systems/ds4/assets/icons/game-icons/delapouite/pool-dive.svg',
wakeUp: "systems/ds4/assets/icons/game-icons/delapouite/alarm-clock.svg", wakeUp: 'systems/ds4/assets/icons/game-icons/delapouite/alarm-clock.svg',
workMechanism: "systems/ds4/assets/icons/game-icons/lorc/lever.svg", workMechanism: 'systems/ds4/assets/icons/game-icons/lorc/lever.svg',
}, },
}, },
@ -437,16 +437,16 @@ export const DS4 = {
* Profile info types for handlebars of a character * Profile info types for handlebars of a character
*/ */
characterProfileDTypes: { characterProfileDTypes: {
biography: "String", biography: 'String',
gender: "String", gender: 'String',
birthday: "String", birthday: 'String',
birthplace: "String", birthplace: 'String',
age: "Number", age: 'Number',
height: "Number", height: 'Number',
hairColor: "String", hairColor: 'String',
weight: "Number", weight: 'Number',
eyeColor: "String", eyeColor: 'String',
specialCharacteristics: "String", specialCharacteristics: 'String',
}, },
/** /**

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
export function evaluateCheck( export function evaluateCheck(
dice: number[], dice: number[],
@ -41,7 +41,7 @@ function assignSubChecksToDice(
const requiredNumberOfDice = getRequiredNumberOfDice(checkTargetNumber); const requiredNumberOfDice = getRequiredNumberOfDice(checkTargetNumber);
if (dice.length !== requiredNumberOfDice || requiredNumberOfDice < 1) { if (dice.length !== requiredNumberOfDice || requiredNumberOfDice < 1) {
throw new Error(getGame().i18n.localize("DS4.ErrorInvalidNumberOfDice")); throw new Error(getGame().i18n.localize('DS4.ErrorInvalidNumberOfDice'));
} }
const checkTargetNumberForLastSubCheck = checkTargetNumber - 20 * (requiredNumberOfDice - 1); const checkTargetNumberForLastSubCheck = checkTargetNumber - 20 * (requiredNumberOfDice - 1);
@ -68,8 +68,7 @@ function findIndexOfSmallestNonCoup(dice: number[], maximumCoupResult: number):
.map((die, index): [number, number] => [die, index]) .map((die, index): [number, number] => [die, index])
.filter((indexedDie) => indexedDie[0] > maximumCoupResult) .filter((indexedDie) => indexedDie[0] > maximumCoupResult)
.reduce( .reduce(
(smallestIndexedDie, indexedDie) => (smallestIndexedDie, indexedDie) => (indexedDie[0] < smallestIndexedDie[0] ? indexedDie : smallestIndexedDie),
indexedDie[0] < smallestIndexedDie[0] ? indexedDie : smallestIndexedDie,
[Infinity, -1], [Infinity, -1],
)[1]; )[1];
} }
@ -90,7 +89,7 @@ function shouldUseCoupForLastSubCheck(
!Number.isInteger(indexOfSmallestNonCoup) || !Number.isInteger(indexOfSmallestNonCoup) ||
smallestNonCoup === undefined smallestNonCoup === undefined
) { ) {
throw new Error("Received an invalid value for the parameter indexOfSmallestNonCoup or indexOfFirstCoup,"); throw new Error('Received an invalid value for the parameter indexOfSmallestNonCoup or indexOfFirstCoup,');
} }
return ( return (
indexOfFirstCoup !== -1 && indexOfFirstCoup !== -1 &&

View file

@ -3,9 +3,9 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DialogWithListeners } from "../apps/dialog-with-listeners"; import { DialogWithListeners } from '../apps/dialog-with-listeners';
import { DS4 } from "../config"; import { DS4 } from '../config';
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
/** @typedef {"publicroll" | "gmroll" | "gmroll" | "selfroll"} RollModes */ /** @typedef {"publicroll" | "gmroll" | "gmroll" | "selfroll"} RollModes */
@ -51,7 +51,7 @@ class CheckFactory {
maximumCoupResult: 1, maximumCoupResult: 1,
minimumFumbleResult: 20, minimumFumbleResult: 20,
useSlayingDice: false, useSlayingDice: false,
rollMode: "publicroll", rollMode: 'publicroll',
}; };
} }
@ -59,9 +59,7 @@ class CheckFactory {
* Execute this check factory. * Execute this check factory.
* @returns {Promise<ChatMessage | undefined>} A promise that resolves to the created chat message for the roll */ * @returns {Promise<ChatMessage | undefined>} A promise that resolves to the created chat message for the roll */
async execute() { async execute() {
const innerFormula = ["ds", this.createCheckTargetNumberModifier(), this.createCoupFumbleModifier()].filterJoin( const innerFormula = ['ds', this.createCheckTargetNumberModifier(), this.createCoupFumbleModifier()].filterJoin('');
"",
);
const formula = this.#options.useSlayingDice ? `{${innerFormula}}x` : innerFormula; const formula = this.#options.useSlayingDice ? `{${innerFormula}}x` : innerFormula;
const roll = Roll.create(formula); const roll = Roll.create(formula);
const speaker = this.#options.speaker ?? ChatMessage.getSpeaker(); const speaker = this.#options.speaker ?? ChatMessage.getSpeaker();
@ -82,7 +80,7 @@ class CheckFactory {
*/ */
createCheckTargetNumberModifier() { createCheckTargetNumberModifier() {
const totalCheckTargetNumber = this.#checkTargetNumber + this.#checkModifier; const totalCheckTargetNumber = this.#checkTargetNumber + this.#checkModifier;
return totalCheckTargetNumber >= 0 ? `v${this.#checkTargetNumber + this.#checkModifier}` : "v0"; return totalCheckTargetNumber >= 0 ? `v${this.#checkTargetNumber + this.#checkModifier}` : 'v0';
} }
/** /**
@ -96,7 +94,7 @@ class CheckFactory {
this.#options.maximumCoupResult !== this.constructor.defaultOptions.maximumCoupResult; this.#options.maximumCoupResult !== this.constructor.defaultOptions.maximumCoupResult;
if (isMinimumFumbleResultRequired || isMaximumCoupResultRequired) { if (isMinimumFumbleResultRequired || isMaximumCoupResultRequired) {
return `c${this.#options.maximumCoupResult ?? ""}:${this.#options.minimumFumbleResult ?? ""}`; return `c${this.#options.maximumCoupResult ?? ''}:${this.#options.minimumFumbleResult ?? ''}`;
} else { } else {
return null; return null;
} }
@ -119,7 +117,7 @@ export async function createCheckRoll(checkTargetNumber, options = {}) {
const newOptions = { const newOptions = {
maximumCoupResult: interactiveRollData.maximumCoupResult ?? options.maximumCoupResult, maximumCoupResult: interactiveRollData.maximumCoupResult ?? options.maximumCoupResult,
minimumFumbleResult: interactiveRollData.minimumFumbleResult ?? options.minimumFumbleResult, minimumFumbleResult: interactiveRollData.minimumFumbleResult ?? options.minimumFumbleResult,
useSlayingDice: getGame().settings.get("ds4", "useSlayingDiceForAutomatedChecks"), useSlayingDice: getGame().settings.get('ds4', 'useSlayingDiceForAutomatedChecks'),
rollMode: interactiveRollData.rollMode ?? options.rollMode, rollMode: interactiveRollData.rollMode ?? options.rollMode,
flavor: options.flavor, flavor: options.flavor,
flavorData: options.flavorData, flavorData: options.flavorData,
@ -148,15 +146,15 @@ export async function createCheckRoll(checkTargetNumber, options = {}) {
* @returns {Promise<Partial<IntermediateInteractiveRollData>>} A promise that resolves to the data given by the user. * @returns {Promise<Partial<IntermediateInteractiveRollData>>} A promise that resolves to the data given by the user.
*/ */
async function askForInteractiveRollData(checkTargetNumber, options = {}, { template, title } = {}) { async function askForInteractiveRollData(checkTargetNumber, options = {}, { template, title } = {}) {
const usedTemplate = template ?? "systems/ds4/templates/dialogs/roll-options.hbs"; const usedTemplate = template ?? 'systems/ds4/templates/dialogs/roll-options.hbs';
const usedTitle = title ?? getGame().i18n.localize("DS4.DialogRollOptionsDefaultTitle"); const usedTitle = title ?? getGame().i18n.localize('DS4.DialogRollOptionsDefaultTitle');
const id = foundry.utils.randomID(); const id = foundry.utils.randomID();
const templateData = { const templateData = {
title: usedTitle, title: usedTitle,
checkTargetNumber: checkTargetNumber, checkTargetNumber: checkTargetNumber,
maximumCoupResult: options.maximumCoupResult ?? this.constructor.defaultOptions.maximumCoupResult, maximumCoupResult: options.maximumCoupResult ?? this.constructor.defaultOptions.maximumCoupResult,
minimumFumbleResult: options.minimumFumbleResult ?? this.constructor.defaultOptions.minimumFumbleResult, minimumFumbleResult: options.minimumFumbleResult ?? this.constructor.defaultOptions.minimumFumbleResult,
rollMode: options.rollMode ?? getGame().settings.get("core", "rollMode"), rollMode: options.rollMode ?? getGame().settings.get('core', 'rollMode'),
rollModes: CONFIG.Dice.rollModes, rollModes: CONFIG.Dice.rollModes,
checkModifiers: Object.entries(DS4.i18n.checkModifiers).map(([key, translation]) => { checkModifiers: Object.entries(DS4.i18n.checkModifiers).map(([key, translation]) => {
if (key in DS4.checkModifiers) { if (key in DS4.checkModifiers) {
@ -178,21 +176,21 @@ async function askForInteractiveRollData(checkTargetNumber, options = {}, { temp
buttons: { buttons: {
ok: { ok: {
icon: '<i class="fas fa-check"></i>', icon: '<i class="fas fa-check"></i>',
label: getGame().i18n.localize("DS4.GenericOkButton"), label: getGame().i18n.localize('DS4.GenericOkButton'),
callback: (html) => { callback: (html) => {
if (!("jquery" in html)) { if (!('jquery' in html)) {
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedHtmlType", { getGame().i18n.format('DS4.ErrorUnexpectedHtmlType', {
exType: "JQuery", exType: 'JQuery',
realType: "HTMLElement", realType: 'HTMLElement',
}), }),
); );
} else { } else {
const innerForm = html[0]?.querySelector("form"); const innerForm = html[0]?.querySelector('form');
if (!innerForm) { if (!innerForm) {
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorCouldNotFindHtmlElement", { getGame().i18n.format('DS4.ErrorCouldNotFindHtmlElement', {
htmlElement: "form", htmlElement: 'form',
}), }),
); );
} }
@ -202,26 +200,21 @@ async function askForInteractiveRollData(checkTargetNumber, options = {}, { temp
}, },
cancel: { cancel: {
icon: '<i class="fas fa-times"></i>', icon: '<i class="fas fa-times"></i>',
label: getGame().i18n.localize("DS4.GenericCancelButton"), label: getGame().i18n.localize('DS4.GenericCancelButton'),
}, },
}, },
default: "ok", default: 'ok',
}, },
{ {
activateAdditionalListeners: (html, app) => { activateAdditionalListeners: (html, app) => {
const checkModifierCustomFormGroup = html const checkModifierCustomFormGroup = html.find(`#check-modifier-custom-${id}`).parent('.form-group');
.find(`#check-modifier-custom-${id}`) html.find(`#check-modifier-${id}`).on('change', (event) => {
.parent(".form-group"); if (event.currentTarget.value === 'custom' && checkModifierCustomFormGroup.hasClass('ds4-hidden')) {
html.find(`#check-modifier-${id}`).on("change", (event) => { checkModifierCustomFormGroup.removeClass('ds4-hidden');
if ( app.setPosition({ height: 'auto' });
event.currentTarget.value === "custom" && } else if (!checkModifierCustomFormGroup.hasClass('ds4-hidden')) {
checkModifierCustomFormGroup.hasClass("ds4-hidden") checkModifierCustomFormGroup.addClass('ds4-hidden');
) { app.setPosition({ height: 'auto' });
checkModifierCustomFormGroup.removeClass("ds4-hidden");
app.setPosition({ height: "auto" });
} else if (!checkModifierCustomFormGroup.hasClass("ds4-hidden")) {
checkModifierCustomFormGroup.addClass("ds4-hidden");
app.setPosition({ height: "auto" });
} }
}); });
}, },
@ -239,17 +232,17 @@ async function askForInteractiveRollData(checkTargetNumber, options = {}, { temp
* @returns {Partial<IntermediateInteractiveRollData>} * @returns {Partial<IntermediateInteractiveRollData>}
*/ */
function parseDialogFormData(formData) { function parseDialogFormData(formData) {
const chosenCheckTargetNumber = parseInt(formData["check-target-number"]?.value); const chosenCheckTargetNumber = parseInt(formData['check-target-number']?.value);
const chosenCheckModifier = formData["check-modifier"]?.value; const chosenCheckModifier = formData['check-modifier']?.value;
const chosenCheckModifierValue = const chosenCheckModifierValue =
chosenCheckModifier === "custom" chosenCheckModifier === 'custom'
? parseInt(formData["check-modifier-custom"]?.value) ? parseInt(formData['check-modifier-custom']?.value)
: parseInt(chosenCheckModifier); : parseInt(chosenCheckModifier);
const chosenMaximumCoupResult = parseInt(formData["maximum-coup-result"]?.value); const chosenMaximumCoupResult = parseInt(formData['maximum-coup-result']?.value);
const chosenMinimumFumbleResult = parseInt(formData["minimum-fumble-result"]?.value); const chosenMinimumFumbleResult = parseInt(formData['minimum-fumble-result']?.value);
const chosenRollMode = formData["roll-mode"]?.value; const chosenRollMode = formData['roll-mode']?.value;
return { return {
checkTargetNumber: Number.isSafeInteger(chosenCheckTargetNumber) ? chosenCheckTargetNumber : undefined, checkTargetNumber: Number.isSafeInteger(chosenCheckTargetNumber) ? chosenCheckTargetNumber : undefined,

View file

@ -3,8 +3,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
import { evaluateCheck, getRequiredNumberOfDice } from "./check-evaluation"; import { evaluateCheck, getRequiredNumberOfDice } from './check-evaluation';
/** /**
* Implements DS4 Checks as an emulated "dice throw". * Implements DS4 Checks as an emulated "dice throw".
@ -25,8 +25,8 @@ export class DS4Check extends DiceTerm {
}); });
// Parse and store check target number // Parse and store check target number
const checkTargetNumberModifier = this.modifiers.filter((m) => m[0] === "v")[0]; const checkTargetNumberModifier = this.modifiers.filter((m) => m[0] === 'v')[0];
const ctnRgx = new RegExp("v([0-9]+)?"); const ctnRgx = new RegExp('v([0-9]+)?');
const ctnMatch = checkTargetNumberModifier?.match(ctnRgx); const ctnMatch = checkTargetNumberModifier?.match(ctnRgx);
if (ctnMatch) { if (ctnMatch) {
const [parseCheckTargetNumber] = ctnMatch.slice(1); const [parseCheckTargetNumber] = ctnMatch.slice(1);
@ -38,8 +38,8 @@ export class DS4Check extends DiceTerm {
this.number = getRequiredNumberOfDice(this.checkTargetNumber); this.number = getRequiredNumberOfDice(this.checkTargetNumber);
// Parse and store maximumCoupResult and minimumFumbleResult // Parse and store maximumCoupResult and minimumFumbleResult
const coupFumbleModifier = this.modifiers.filter((m) => m[0] === "c")[0]; const coupFumbleModifier = this.modifiers.filter((m) => m[0] === 'c')[0];
const cfmRgx = new RegExp("c([0-9]+)?(:([0-9]+))?"); const cfmRgx = new RegExp('c([0-9]+)?(:([0-9]+))?');
const cfmMatch = coupFumbleModifier?.match(cfmRgx); const cfmMatch = coupFumbleModifier?.match(cfmRgx);
if (cfmMatch) { if (cfmMatch) {
const parseMaximumCoupResult = cfmMatch[1]; const parseMaximumCoupResult = cfmMatch[1];
@ -51,11 +51,11 @@ export class DS4Check extends DiceTerm {
? parseInt(parseMinimumFumbleResult) ? parseInt(parseMinimumFumbleResult)
: DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT; : DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT;
if (this.minimumFumbleResult <= this.maximumCoupResult) if (this.minimumFumbleResult <= this.maximumCoupResult)
throw new SyntaxError(getGame().i18n.localize("DS4.ErrorDiceCoupFumbleOverlap")); throw new SyntaxError(getGame().i18n.localize('DS4.ErrorDiceCoupFumbleOverlap'));
} }
// Parse and store no fumble // Parse and store no fumble
const noFumbleModifier = this.modifiers.filter((m) => m[0] === "n")[0]; const noFumbleModifier = this.modifiers.filter((m) => m[0] === 'n')[0];
if (noFumbleModifier) { if (noFumbleModifier) {
this.canFumble = false; this.canFumble = false;
} }
@ -76,7 +76,7 @@ export class DS4Check extends DiceTerm {
/** @override */ /** @override */
get expression() { get expression() {
return `ds${this.modifiers.join("")}`; return `ds${this.modifiers.join('')}`;
} }
/** @override */ /** @override */
@ -121,13 +121,13 @@ export class DS4Check extends DiceTerm {
* @override * @override
*/ */
getResultCSS(result) { getResultCSS(result) {
return super.getResultCSS(result).filter((cssClass) => cssClass !== "min" && cssClass !== "max"); return super.getResultCSS(result).filter((cssClass) => cssClass !== 'min' && cssClass !== 'max');
} }
static DEFAULT_CHECK_TARGET_NUMBER = 10; static DEFAULT_CHECK_TARGET_NUMBER = 10;
static DEFAULT_MAXIMUM_COUP_RESULT = 1; static DEFAULT_MAXIMUM_COUP_RESULT = 1;
static DEFAULT_MINIMUM_FUMBLE_RESULT = 20; static DEFAULT_MINIMUM_FUMBLE_RESULT = 20;
static DENOMINATION = "s"; static DENOMINATION = 's';
static MODIFIERS = { static MODIFIERS = {
c: () => undefined, // Modifier is consumed in constructor for maximumCoupResult / minimumFumbleResult c: () => undefined, // Modifier is consumed in constructor for maximumCoupResult / minimumFumbleResult
v: () => undefined, // Modifier is consumed in constructor for checkTargetNumber v: () => undefined, // Modifier is consumed in constructor for checkTargetNumber

View file

@ -2,12 +2,12 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
import { DS4Check } from "./check"; import { DS4Check } from './check';
export class DS4Roll extends Roll { export class DS4Roll extends Roll {
/** @override */ /** @override */
static CHAT_TEMPLATE = "systems/ds4/templates/dice/roll.hbs"; static CHAT_TEMPLATE = 'systems/ds4/templates/dice/roll.hbs';
/** /**
* @override * @override
@ -21,11 +21,11 @@ export class DS4Roll extends Roll {
const isCoup = firstDiceTerm instanceof DS4Check && firstDiceTerm.coup; const isCoup = firstDiceTerm instanceof DS4Check && firstDiceTerm.coup;
const isFumble = firstDiceTerm instanceof DS4Check && firstDiceTerm.fumble; const isFumble = firstDiceTerm instanceof DS4Check && firstDiceTerm.fumble;
const chatData = { const chatData = {
formula: isPrivate ? "???" : this._formula, formula: isPrivate ? '???' : this._formula,
flavor: isPrivate ? null : flavor, flavor: isPrivate ? null : flavor,
user: getGame().user?.id, user: getGame().user?.id,
tooltip: isPrivate ? "" : await this.getTooltip(), tooltip: isPrivate ? '' : await this.getTooltip(),
total: isPrivate ? "?" : Math.round((this.total ?? 0) * 100) / 100, total: isPrivate ? '?' : Math.round((this.total ?? 0) * 100) / 100,
isCoup: isPrivate ? null : isCoup, isCoup: isPrivate ? null : isCoup,
isFumble: isPrivate ? null : isFumble, isFumble: isPrivate ? null : isFumble,
}; };

View file

@ -3,8 +3,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
import { DS4Check } from "./check"; import { DS4Check } from './check';
export function registerSlayingDiceModifier() { export function registerSlayingDiceModifier() {
PoolTerm.MODIFIERS.x = slay; PoolTerm.MODIFIERS.x = slay;
@ -31,6 +31,6 @@ function slay(modifier) {
this.results.push({ result: additionalRoll.total ?? 0, active: true }); this.results.push({ result: additionalRoll.total ?? 0, active: true });
this.terms.push(formula); this.terms.push(formula);
} }
if (checked > 1000) throw new Error(getGame().i18n.localize("DS4.ErrorSlayingDiceRecursionLimitExceeded")); if (checked > 1000) throw new Error(getGame().i18n.localize('DS4.ErrorSlayingDiceRecursionLimitExceeded'));
} }
} }

View file

@ -2,8 +2,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { mathEvaluator } from "../expression-evaluation/evaluator"; import { mathEvaluator } from '../expression-evaluation/evaluator';
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
/** /**
* @typedef {object} ItemEffectConfig * @typedef {object} ItemEffectConfig
@ -26,7 +26,7 @@ export class DS4ActiveEffect extends ActiveEffect {
/** /**
* A fallback icon that can be used if no icon is defined for the effect. * A fallback icon that can be used if no icon is defined for the effect.
*/ */
static FALLBACK_ICON = "icons/svg/aura.svg"; static FALLBACK_ICON = 'icons/svg/aura.svg';
/** /**
* A cached reference to the source document to avoid recurring database lookups * A cached reference to the source document to avoid recurring database lookups

View file

@ -5,9 +5,9 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4 } from "../../config"; import { DS4 } from '../../config';
import type { ModifiableDataBaseTotal, ResourceDataBaseTotalMax } from "../common/common-data"; import type { ModifiableDataBaseTotal, ResourceDataBaseTotalMax } from '../common/common-data';
export interface DS4ActorDataPropertiesDataBase { export interface DS4ActorDataPropertiesDataBase {
attributes: DS4ActorDataPropertiesDataAttributes; attributes: DS4ActorDataPropertiesDataAttributes;
@ -25,7 +25,7 @@ type DS4ActorDataPropertiesDataAttributes = {
type DS4ActorDataPropertiesDataTraits = { [Key in keyof typeof DS4.i18n.traits]: ModifiableDataBaseTotal<number> }; type DS4ActorDataPropertiesDataTraits = { [Key in keyof typeof DS4.i18n.traits]: ModifiableDataBaseTotal<number> };
type DS4ActorDataPropertiesDataCombatValues = { type DS4ActorDataPropertiesDataCombatValues = {
[Key in keyof typeof DS4.i18n.combatValues]: Key extends "hitPoints" [Key in keyof typeof DS4.i18n.combatValues]: Key extends 'hitPoints'
? ResourceDataBaseTotalMax<number> ? ResourceDataBaseTotalMax<number>
: ModifiableDataBaseTotal<number>; : ModifiableDataBaseTotal<number>;
}; };

View file

@ -2,8 +2,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4CharacterDataProperties } from "./character/character-data-properties"; import type { DS4CharacterDataProperties } from './character/character-data-properties';
import type { DS4CreatureDataProperties } from "./creature/creature-data-properties"; import type { DS4CreatureDataProperties } from './creature/creature-data-properties';
declare global { declare global {
interface DataConfig { interface DataConfig {

View file

@ -5,9 +5,9 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4 } from "../../config"; import { DS4 } from '../../config';
import type { ModifiableData, ModifiableDataBase, ResourceData } from "../common/common-data"; import type { ModifiableData, ModifiableDataBase, ResourceData } from '../common/common-data';
export interface DS4ActorDataSourceDataBase { export interface DS4ActorDataSourceDataBase {
attributes: DS4ActorDataSourceDataAttributes; attributes: DS4ActorDataSourceDataAttributes;
@ -32,9 +32,7 @@ export function isTrait(value: unknown): value is Trait {
} }
type DS4ActorDataSourceDataCombatValues = { type DS4ActorDataSourceDataCombatValues = {
[Key in keyof typeof DS4.i18n.combatValues]: Key extends "hitPoints" [Key in keyof typeof DS4.i18n.combatValues]: Key extends 'hitPoints' ? ResourceData<number> : ModifiableData<number>;
? ResourceData<number>
: ModifiableData<number>;
}; };
type CombatValue = keyof DS4ActorDataSourceDataCombatValues; type CombatValue = keyof DS4ActorDataSourceDataCombatValues;

View file

@ -2,8 +2,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4CharacterDataSource } from "./character/character-data-source"; import type { DS4CharacterDataSource } from './character/character-data-source';
import type { DS4CreatureDataSource } from "./creature/creature-data-source"; import type { DS4CreatureDataSource } from './creature/creature-data-source';
declare global { declare global {
interface SourceConfig { interface SourceConfig {

View file

@ -3,13 +3,13 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4 } from "../../config"; import { DS4 } from '../../config';
import { createCheckRoll } from "../../dice/check-factory"; import { createCheckRoll } from '../../dice/check-factory';
import { mathEvaluator } from "../../expression-evaluation/evaluator"; import { mathEvaluator } from '../../expression-evaluation/evaluator';
import { logger } from "../../utils/logger"; import { logger } from '../../utils/logger';
import { getGame } from "../../utils/utils"; import { getGame } from '../../utils/utils';
import { DS4ActiveEffect } from "../active-effect"; import { DS4ActiveEffect } from '../active-effect';
import { isAttribute, isTrait } from "./actor-data-source-base"; import { isAttribute, isTrait } from './actor-data-source-base';
/** /**
* The Actor class for DS4 * The Actor class for DS4
@ -39,9 +39,7 @@ export class DS4Actor extends Actor {
maximumCoupResult: 1, maximumCoupResult: 1,
}; };
Object.values(this.system.attributes).forEach( Object.values(this.system.attributes).forEach((attribute) => (attribute.total = attribute.base + attribute.mod));
(attribute) => (attribute.total = attribute.base + attribute.mod),
);
Object.values(this.system.traits).forEach((trait) => (trait.total = trait.base + trait.mod)); Object.values(this.system.traits).forEach((trait) => (trait.total = trait.base + trait.mod));
} }
@ -85,11 +83,11 @@ export class DS4Actor extends Actor {
const shouldEffectBeAppliedToItem = (effect, item) => { const shouldEffectBeAppliedToItem = (effect, item) => {
const { applyToItems, itemName, condition } = effect.flags.ds4?.itemEffectConfig ?? {}; const { applyToItems, itemName, condition } = effect.flags.ds4?.itemEffectConfig ?? {};
if (!applyToItems || (itemName !== undefined && itemName !== "" && itemName !== item.name)) { if (!applyToItems || (itemName !== undefined && itemName !== '' && itemName !== item.name)) {
return false; return false;
} }
if (condition !== undefined && condition !== "") { if (condition !== undefined && condition !== '') {
try { try {
const replacedCondition = DS4Actor.replaceFormulaData(condition, { item, actor: this, effect }); const replacedCondition = DS4Actor.replaceFormulaData(condition, { item, actor: this, effect });
return replacedCondition !== undefined ? Boolean(mathEvaluator.evaluate(replacedCondition)) : false; return replacedCondition !== undefined ? Boolean(mathEvaluator.evaluate(replacedCondition)) : false;
@ -158,7 +156,7 @@ export class DS4Actor extends Actor {
this.applyActiveEffectsToItem(item); this.applyActiveEffectsToItem(item);
} }
for (const item of this.items) { for (const item of this.items) {
if (item.type === "talent") continue; if (item.type === 'talent') continue;
this.applyActiveEffectsToItem(item); this.applyActiveEffectsToItem(item);
} }
} }
@ -184,8 +182,7 @@ export class DS4Actor extends Actor {
this, this,
this.actorEffects, this.actorEffects,
(change) => (change) =>
!this.derivedDataProperties.includes(change.key) && !this.derivedDataProperties.includes(change.key) && !this.finalDerivedDataProperties.includes(change.key),
!this.finalDerivedDataProperties.includes(change.key),
).forEach(this.newStatuses.add.bind(this.newStatuses)); ).forEach(this.newStatuses.add.bind(this.newStatuses));
} }
@ -218,7 +215,7 @@ export class DS4Actor extends Actor {
(combatValue) => `system.combatValues.${combatValue}.total`, (combatValue) => `system.combatValues.${combatValue}.total`,
); );
const checkProperties = Object.keys(DS4.i18n.checks) const checkProperties = Object.keys(DS4.i18n.checks)
.filter((check) => check !== "defend") .filter((check) => check !== 'defend')
.map((check) => `system.checks.${check}`); .map((check) => `system.checks.${check}`);
return combatValueProperties.concat(checkProperties); return combatValueProperties.concat(checkProperties);
} }
@ -254,7 +251,7 @@ export class DS4Actor extends Actor {
Object.values(this.system.attributes).forEach((attribute) => (attribute.total = Math.ceil(attribute.total))); Object.values(this.system.attributes).forEach((attribute) => (attribute.total = Math.ceil(attribute.total)));
Object.values(this.system.traits).forEach((trait) => (trait.total = Math.ceil(trait.total))); Object.values(this.system.traits).forEach((trait) => (trait.total = Math.ceil(trait.total)));
Object.entries(this.system.combatValues) Object.entries(this.system.combatValues)
.filter(([key]) => key !== "movement") .filter(([key]) => key !== 'movement')
.forEach(([, combatValue]) => (combatValue.total = Math.ceil(combatValue.total))); .forEach(([, combatValue]) => (combatValue.total = Math.ceil(combatValue.total)));
Object.keys(this.system.checks).forEach((key) => { Object.keys(this.system.checks).forEach((key) => {
this.system.checks[key] = Math.ceil(this.system.checks[key]); this.system.checks[key] = Math.ceil(this.system.checks[key]);
@ -270,7 +267,7 @@ export class DS4Actor extends Actor {
* @type {string[]} * @type {string[]}
*/ */
get finalDerivedDataProperties() { get finalDerivedDataProperties() {
return ["system.combatValues.hitPoints.max", "system.checks.defend"]; return ['system.combatValues.hitPoints.max', 'system.checks.defend'];
} }
/** /**
@ -278,7 +275,7 @@ export class DS4Actor extends Actor {
* @type {import("../item/item-data-source").ItemType[]} * @type {import("../item/item-data-source").ItemType[]}
*/ */
get ownableItemTypes() { get ownableItemTypes() {
return ["weapon", "armor", "shield", "equipment", "loot", "spell"]; return ['weapon', 'armor', 'shield', 'equipment', 'loot', 'spell'];
} }
/** /**
@ -330,7 +327,7 @@ export class DS4Actor extends Actor {
*/ */
get armorValueSpellMalusOfEquippedItems() { get armorValueSpellMalusOfEquippedItems() {
return this.equippedItemsWithArmor return this.equippedItemsWithArmor
.filter((item) => !(item.type === "armor" && ["cloth", "natural"].includes(item.system.armorMaterialType))) .filter((item) => !(item.type === 'armor' && ['cloth', 'natural'].includes(item.system.armorMaterialType)))
.reduce((sum, item) => sum + item.system.armorValue, 0); .reduce((sum, item) => sum + item.system.armorValue, 0);
} }
@ -340,7 +337,7 @@ export class DS4Actor extends Actor {
* @protected * @protected
*/ */
get equippedItemsWithArmor() { get equippedItemsWithArmor() {
return this.items.filter((item) => (item.type === "armor" || item.type === "shield") && item.system.equipped); return this.items.filter((item) => (item.type === 'armor' || item.type === 'shield') && item.system.equipped);
} }
/** /**
@ -360,8 +357,7 @@ export class DS4Actor extends Actor {
disableTraps: system.attributes.mind.total + system.traits.dexterity.total, disableTraps: system.attributes.mind.total + system.traits.dexterity.total,
featOfStrength: system.attributes.body.total + system.traits.strength.total, featOfStrength: system.attributes.body.total + system.traits.strength.total,
flirt: system.attributes.mind.total + system.traits.aura.total, flirt: system.attributes.mind.total + system.traits.aura.total,
haggle: haggle: system.attributes.mind.total + Math.max(system.traits.intellect.total, system.traits.intellect.total),
system.attributes.mind.total + Math.max(system.traits.intellect.total, system.traits.intellect.total),
hide: system.attributes.mobility.total + system.traits.agility.total, hide: system.attributes.mobility.total + system.traits.agility.total,
identifyMagic: system.attributes.mind.total + system.traits.intellect.total, identifyMagic: system.attributes.mind.total + system.traits.intellect.total,
jump: system.attributes.mobility.total + system.traits.agility.total, jump: system.attributes.mobility.total + system.traits.agility.total,
@ -408,7 +404,7 @@ export class DS4Actor extends Actor {
} }
// Call a hook to handle token resource bar updates // Call a hook to handle token resource bar updates
const allowed = Hooks.call("modifyTokenAttribute", { attribute, value, isDelta, isBar }, updates); const allowed = Hooks.call('modifyTokenAttribute', { attribute, value, isDelta, isBar }, updates);
return allowed !== false ? this.update(updates) : this; return allowed !== false ? this.update(updates) : this;
} }
@ -421,10 +417,10 @@ export class DS4Actor extends Actor {
async rollCheck(check, options = {}) { async rollCheck(check, options = {}) {
const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker }); const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker });
await createCheckRoll(this.system.checks[check], { await createCheckRoll(this.system.checks[check], {
rollMode: getGame().settings.get("core", "rollMode"), rollMode: getGame().settings.get('core', 'rollMode'),
maximumCoupResult: this.system.rolling.maximumCoupResult, maximumCoupResult: this.system.rolling.maximumCoupResult,
minimumFumbleResult: this.system.rolling.minimumFumbleResult, minimumFumbleResult: this.system.rolling.minimumFumbleResult,
flavor: "DS4.ActorCheckFlavor", flavor: 'DS4.ActorCheckFlavor',
flavorData: { actor: speaker.alias ?? this.name, check: DS4.i18nKeys.checks[check] }, flavorData: { actor: speaker.alias ?? this.name, check: DS4.i18nKeys.checks[check] },
speaker, speaker,
}); });
@ -445,10 +441,10 @@ export class DS4Actor extends Actor {
const checkTargetNumber = this.system.attributes[attribute].total + this.system.traits[trait].total; const checkTargetNumber = this.system.attributes[attribute].total + this.system.traits[trait].total;
const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker }); const speaker = ChatMessage.getSpeaker({ actor: this, ...options.speaker });
await createCheckRoll(checkTargetNumber, { await createCheckRoll(checkTargetNumber, {
rollMode: getGame().settings.get("core", "rollMode"), rollMode: getGame().settings.get('core', 'rollMode'),
maximumCoupResult: this.system.rolling.maximumCoupResult, maximumCoupResult: this.system.rolling.maximumCoupResult,
minimumFumbleResult: this.system.rolling.minimumFumbleResult, minimumFumbleResult: this.system.rolling.minimumFumbleResult,
flavor: "DS4.ActorGenericCheckFlavor", flavor: 'DS4.ActorGenericCheckFlavor',
flavorData: { flavorData: {
actor: speaker.alias ?? this.name, actor: speaker.alias ?? this.name,
attribute: DS4.i18n.attributes[attribute], attribute: DS4.i18n.attributes[attribute],
@ -464,14 +460,14 @@ export class DS4Actor extends Actor {
* @protected * @protected
*/ */
async selectAttributeAndTrait() { async selectAttributeAndTrait() {
const attributeIdentifier = "attribute-trait-selection-attribute"; const attributeIdentifier = 'attribute-trait-selection-attribute';
const traitIdentifier = "attribute-trait-selection-trait"; const traitIdentifier = 'attribute-trait-selection-trait';
return Dialog.prompt({ return Dialog.prompt({
title: getGame().i18n.localize("DS4.DialogAttributeTraitSelection"), title: getGame().i18n.localize('DS4.DialogAttributeTraitSelection'),
content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { content: await renderTemplate('systems/ds4/templates/dialogs/simple-select-form.hbs', {
selects: [ selects: [
{ {
label: getGame().i18n.localize("DS4.Attribute"), label: getGame().i18n.localize('DS4.Attribute'),
identifier: attributeIdentifier, identifier: attributeIdentifier,
options: Object.fromEntries( options: Object.fromEntries(
Object.entries(DS4.i18n.attributes).map(([attribute, translation]) => [ Object.entries(DS4.i18n.attributes).map(([attribute, translation]) => [
@ -481,7 +477,7 @@ export class DS4Actor extends Actor {
), ),
}, },
{ {
label: getGame().i18n.localize("DS4.Trait"), label: getGame().i18n.localize('DS4.Trait'),
identifier: traitIdentifier, identifier: traitIdentifier,
options: Object.fromEntries( options: Object.fromEntries(
Object.entries(DS4.i18n.traits).map(([trait, translation]) => [ Object.entries(DS4.i18n.traits).map(([trait, translation]) => [
@ -492,27 +488,27 @@ export class DS4Actor extends Actor {
}, },
], ],
}), }),
label: getGame().i18n.localize("DS4.GenericOkButton"), label: getGame().i18n.localize('DS4.GenericOkButton'),
callback: (html) => { callback: (html) => {
const selectedAttribute = html.find(`#${attributeIdentifier}`).val(); const selectedAttribute = html.find(`#${attributeIdentifier}`).val();
if (!isAttribute(selectedAttribute)) { if (!isAttribute(selectedAttribute)) {
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedAttribute", { getGame().i18n.format('DS4.ErrorUnexpectedAttribute', {
actualAttribute: selectedAttribute, actualAttribute: selectedAttribute,
expectedTypes: Object.keys(DS4.i18n.attributes) expectedTypes: Object.keys(DS4.i18n.attributes)
.map((attribute) => `'${attribute}'`) .map((attribute) => `'${attribute}'`)
.join(", "), .join(', '),
}), }),
); );
} }
const selectedTrait = html.find(`#${traitIdentifier}`).val(); const selectedTrait = html.find(`#${traitIdentifier}`).val();
if (!isTrait(selectedTrait)) { if (!isTrait(selectedTrait)) {
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedTrait", { getGame().i18n.format('DS4.ErrorUnexpectedTrait', {
actualTrait: selectedTrait, actualTrait: selectedTrait,
expectedTypes: Object.keys(DS4.i18n.traits) expectedTypes: Object.keys(DS4.i18n.traits)
.map((attribute) => `'${attribute}'`) .map((attribute) => `'${attribute}'`)
.join(", "), .join(', '),
}), }),
); );
} }

View file

@ -2,17 +2,17 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ActorDataPropertiesDataBase } from "../actor-data-properties-base"; import type { DS4ActorDataPropertiesDataBase } from '../actor-data-properties-base';
import type { import type {
DS4CharacterDataSourceDataBaseInfo, DS4CharacterDataSourceDataBaseInfo,
DS4CharacterDataSourceDataCurrency, DS4CharacterDataSourceDataCurrency,
DS4CharacterDataSourceDataProfile, DS4CharacterDataSourceDataProfile,
DS4CharacterDataSourceDataProgression, DS4CharacterDataSourceDataProgression,
DS4CharacterDataSourceDataSlayerPoints, DS4CharacterDataSourceDataSlayerPoints,
} from "./character-data-source"; } from './character-data-source';
export interface DS4CharacterDataProperties { export interface DS4CharacterDataProperties {
type: "character"; type: 'character';
data: DS4CharacterDataPropertiesData; data: DS4CharacterDataPropertiesData;
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { UsableResource } from "../../common/common-data"; import type { UsableResource } from '../../common/common-data';
import type { DS4ActorDataSourceDataBase } from "../actor-data-source-base"; import type { DS4ActorDataSourceDataBase } from '../actor-data-source-base';
export interface DS4CharacterDataSource { export interface DS4CharacterDataSource {
type: "character"; type: 'character';
data: DS4CharacterDataSourceData; data: DS4CharacterDataSourceData;
} }

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Actor } from "../actor"; import { DS4Actor } from '../actor';
export class DS4Character extends DS4Actor { export class DS4Character extends DS4Actor {
/** @override */ /** @override */
@ -13,11 +13,11 @@ export class DS4Character extends DS4Actor {
/** @override */ /** @override */
get finalDerivedDataProperties() { get finalDerivedDataProperties() {
return [...super.finalDerivedDataProperties, "system.slayerPoints.max"]; return [...super.finalDerivedDataProperties, 'system.slayerPoints.max'];
} }
/** @override */ /** @override */
get ownableItemTypes() { get ownableItemTypes() {
return [...super.ownableItemTypes, "talent", "racialAbility", "language", "alphabet"]; return [...super.ownableItemTypes, 'talent', 'racialAbility', 'language', 'alphabet'];
} }
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ActorDataPropertiesDataBase } from "../actor-data-properties-base"; import type { DS4ActorDataPropertiesDataBase } from '../actor-data-properties-base';
import type { DS4CreatureDataSourceDataBaseInfo } from "./creature-data-source"; import type { DS4CreatureDataSourceDataBaseInfo } from './creature-data-source';
export interface DS4CreatureDataProperties { export interface DS4CreatureDataProperties {
type: "creature"; type: 'creature';
data: DS4CreatureDataPropertiesData; data: DS4CreatureDataPropertiesData;
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4 } from "../../../config"; import type { DS4 } from '../../../config';
import type { DS4ActorDataSourceDataBase } from "../actor-data-source-base"; import type { DS4ActorDataSourceDataBase } from '../actor-data-source-base';
export interface DS4CreatureDataSource { export interface DS4CreatureDataSource {
type: "creature"; type: 'creature';
data: DS4CreatureDataSourceData; data: DS4CreatureDataSourceData;
} }

View file

@ -2,12 +2,12 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Actor } from "../actor"; import { DS4Actor } from '../actor';
import type { ItemType } from "../../item/item-data-source"; import type { ItemType } from '../../item/item-data-source';
export class DS4Creature extends DS4Actor { export class DS4Creature extends DS4Actor {
override get ownableItemTypes(): Array<ItemType> { override get ownableItemTypes(): Array<ItemType> {
return [...super.ownableItemTypes, "specialCreatureAbility"]; return [...super.ownableItemTypes, 'specialCreatureAbility'];
} }
} }

View file

@ -2,10 +2,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../../utils/utils"; import { getGame } from '../../utils/utils';
import { DS4Actor } from "./actor"; import { DS4Actor } from './actor';
import { DS4Character } from "./character/character"; import { DS4Character } from './character/character';
import { DS4Creature } from "./creature/creature"; import { DS4Creature } from './creature/creature';
const handler = { const handler = {
/** /**
@ -14,12 +14,12 @@ const handler = {
*/ */
construct(_, args) { construct(_, args) {
switch (args[0]?.type) { switch (args[0]?.type) {
case "character": case 'character':
return new DS4Character(...args); return new DS4Character(...args);
case "creature": case 'creature':
return new DS4Creature(...args); return new DS4Creature(...args);
default: default:
throw new Error(getGame().i18n.format("DS4.ErrorInvalidActorType", { type: args[0]?.type })); throw new Error(getGame().i18n.format('DS4.ErrorInvalidActorType', { type: args[0]?.type }));
} }
}, },
}; };

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
/** /**
* @typedef {object} DS4ChatMessageFlags * @typedef {object} DS4ChatMessageFlags
@ -22,7 +22,7 @@ export class DS4ChatMessage extends ChatMessage {
const flavorData = Object.fromEntries( const flavorData = Object.fromEntries(
Object.entries(this.flags.ds4?.flavorData ?? {}).map(([key, value]) => [ Object.entries(this.flags.ds4?.flavorData ?? {}).map(([key, value]) => [
key, key,
typeof value === "string" ? game.i18n.localize(value) : value, typeof value === 'string' ? game.i18n.localize(value) : value,
]), ]),
); );
this.flavor = game.i18n.format(this.flavor, flavorData); this.flavor = game.i18n.format(this.flavor, flavorData);

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4AlphabetDataSourceData } from "./alphabet-data-source"; import type { DS4AlphabetDataSourceData } from './alphabet-data-source';
export interface DS4AlphabetDataProperties { export interface DS4AlphabetDataProperties {
type: "alphabet"; type: 'alphabet';
data: DS4AlphabetDataPropertiesData; data: DS4AlphabetDataPropertiesData;
} }

View file

@ -2,10 +2,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataSourceDataBase } from "../item-data-source-base"; import type { DS4ItemDataSourceDataBase } from '../item-data-source-base';
export interface DS4AlphabetDataSource { export interface DS4AlphabetDataSource {
type: "alphabet"; type: 'alphabet';
data: DS4AlphabetDataSourceData; data: DS4AlphabetDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Alphabet extends DS4Item {} export class DS4Alphabet extends DS4Item {}

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4ArmorDataSourceData } from "./armor-data-source"; import type { DS4ArmorDataSourceData } from './armor-data-source';
export interface DS4ArmorDataProperties { export interface DS4ArmorDataProperties {
type: "armor"; type: 'armor';
data: DS4ArmorDataPropertiesData; data: DS4ArmorDataPropertiesData;
} }

View file

@ -2,16 +2,16 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4 } from "../../../config"; import type { DS4 } from '../../../config';
import type { import type {
DS4ItemDataSourceDataBase, DS4ItemDataSourceDataBase,
DS4ItemDataSourceDataEquipable, DS4ItemDataSourceDataEquipable,
DS4ItemDataSourceDataPhysical, DS4ItemDataSourceDataPhysical,
DS4ItemDataSourceDataProtective, DS4ItemDataSourceDataProtective,
} from "../item-data-source-base"; } from '../item-data-source-base';
export interface DS4ArmorDataSource { export interface DS4ArmorDataSource {
type: "armor"; type: 'armor';
data: DS4ArmorDataSourceData; data: DS4ArmorDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Armor extends DS4Item {} export class DS4Armor extends DS4Item {}

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4EquipmentDataSourceData } from "./equipment-data-source"; import type { DS4EquipmentDataSourceData } from './equipment-data-source';
export interface DS4EquipmentDataProperties { export interface DS4EquipmentDataProperties {
type: "equipment"; type: 'equipment';
data: DS4EquipmentDataPropertiesData; data: DS4EquipmentDataPropertiesData;
} }

View file

@ -6,10 +6,10 @@ import type {
DS4ItemDataSourceDataBase, DS4ItemDataSourceDataBase,
DS4ItemDataSourceDataEquipable, DS4ItemDataSourceDataEquipable,
DS4ItemDataSourceDataPhysical, DS4ItemDataSourceDataPhysical,
} from "../item-data-source-base"; } from '../item-data-source-base';
export interface DS4EquipmentDataSource { export interface DS4EquipmentDataSource {
type: "equipment"; type: 'equipment';
data: DS4EquipmentDataSourceData; data: DS4EquipmentDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Equipment extends DS4Item {} export class DS4Equipment extends DS4Item {}

View file

@ -2,17 +2,17 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4AlphabetDataProperties } from "./alphabet/alphabet-data-properties"; import type { DS4AlphabetDataProperties } from './alphabet/alphabet-data-properties';
import type { DS4ArmorDataProperties } from "./armor/armor-data-properties"; import type { DS4ArmorDataProperties } from './armor/armor-data-properties';
import type { DS4EquipmentDataProperties } from "./equipment/equipment-data-properties"; import type { DS4EquipmentDataProperties } from './equipment/equipment-data-properties';
import type { DS4LanguageDataProperties } from "./language/language-data-properties"; import type { DS4LanguageDataProperties } from './language/language-data-properties';
import type { DS4LootDataProperties } from "./loot/loot-data-properties"; import type { DS4LootDataProperties } from './loot/loot-data-properties';
import type { DS4RacialAbilityDataProperties } from "./racial-ability/racial-ability-data-properties"; import type { DS4RacialAbilityDataProperties } from './racial-ability/racial-ability-data-properties';
import type { DS4ShieldDataProperties } from "./shield/shield-data-properties"; import type { DS4ShieldDataProperties } from './shield/shield-data-properties';
import type { DS4SpecialCreatureAbilityDataProperties } from "./special-creature-ability/special-creature-ability-data-properties"; import type { DS4SpecialCreatureAbilityDataProperties } from './special-creature-ability/special-creature-ability-data-properties';
import type { DS4SpellDataProperties } from "./spell/spell-data-properties"; import type { DS4SpellDataProperties } from './spell/spell-data-properties';
import type { DS4TalentDataProperties } from "./talent/talent-data-properties"; import type { DS4TalentDataProperties } from './talent/talent-data-properties';
import type { DS4WeaponDataProperties } from "./weapon/weapon-data-properties"; import type { DS4WeaponDataProperties } from './weapon/weapon-data-properties';
declare global { declare global {
interface DataConfig { interface DataConfig {

View file

@ -4,7 +4,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4 } from "../../config"; import type { DS4 } from '../../config';
export interface DS4ItemDataSourceDataBase { export interface DS4ItemDataSourceDataBase {
description: string; description: string;

View file

@ -2,18 +2,18 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4 } from "../../config"; import type { DS4 } from '../../config';
import type { DS4AlphabetDataSource } from "./alphabet/alphabet-data-source"; import type { DS4AlphabetDataSource } from './alphabet/alphabet-data-source';
import type { DS4ArmorDataSource } from "./armor/armor-data-source"; import type { DS4ArmorDataSource } from './armor/armor-data-source';
import type { DS4EquipmentDataSource } from "./equipment/equipment-data-source"; import type { DS4EquipmentDataSource } from './equipment/equipment-data-source';
import type { DS4LanguageDataSource } from "./language/language-data-source"; import type { DS4LanguageDataSource } from './language/language-data-source';
import type { DS4LootDataSource } from "./loot/loot-data-source"; import type { DS4LootDataSource } from './loot/loot-data-source';
import type { DS4RacialAbilityDataSource } from "./racial-ability/racial-ability-data-source"; import type { DS4RacialAbilityDataSource } from './racial-ability/racial-ability-data-source';
import type { DS4ShieldDataSource } from "./shield/shield-data-source"; import type { DS4ShieldDataSource } from './shield/shield-data-source';
import type { DS4SpecialCreatureAbilityDataSource } from "./special-creature-ability/special-creature-ability-data-source"; import type { DS4SpecialCreatureAbilityDataSource } from './special-creature-ability/special-creature-ability-data-source';
import type { DS4SpellDataSource } from "./spell/spell-data-source"; import type { DS4SpellDataSource } from './spell/spell-data-source';
import type { DS4TalentDataSource } from "./talent/talent-data-source"; import type { DS4TalentDataSource } from './talent/talent-data-source';
import type { DS4WeaponDataSource } from "./weapon/weapon-data-source"; import type { DS4WeaponDataSource } from './weapon/weapon-data-source';
declare global { declare global {
interface SourceConfig { interface SourceConfig {

View file

@ -3,7 +3,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../../utils/utils"; import { getGame } from '../../utils/utils';
/** /**
* The Item class for DS4 * The Item class for DS4
@ -25,7 +25,7 @@ export class DS4Item extends Item {
* @returns {boolean} Whether the item is a non-equpped equibale or not * @returns {boolean} Whether the item is a non-equpped equibale or not
*/ */
isNonEquippedEuipable() { isNonEquippedEuipable() {
return "equipped" in this.system && !this.system.equipped; return 'equipped' in this.system && !this.system.equipped;
} }
/** /**
@ -41,7 +41,7 @@ export class DS4Item extends Item {
* @returns {import("../item/item-data-source").ItemType[]} The rollable item types * @returns {import("../item/item-data-source").ItemType[]} The rollable item types
*/ */
static get rollableItemTypes() { static get rollableItemTypes() {
return ["weapon", "spell"]; return ['weapon', 'spell'];
} }
/** /**
@ -52,6 +52,6 @@ export class DS4Item extends Item {
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
async roll(options = {}) { async roll(options = {}) {
throw new Error(getGame().i18n.format("DS4.ErrorRollingForItemTypeNotPossible", { type: this.type })); throw new Error(getGame().i18n.format('DS4.ErrorRollingForItemTypeNotPossible', { type: this.type }));
} }
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4LanguageDataSourceData } from "./language-data-source"; import type { DS4LanguageDataSourceData } from './language-data-source';
export interface DS4LanguageDataProperties { export interface DS4LanguageDataProperties {
type: "language"; type: 'language';
data: DS4LanguageDataPropertiesData; data: DS4LanguageDataPropertiesData;
} }

View file

@ -2,10 +2,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataSourceDataBase } from "../item-data-source-base"; import type { DS4ItemDataSourceDataBase } from '../item-data-source-base';
export interface DS4LanguageDataSource { export interface DS4LanguageDataSource {
type: "language"; type: 'language';
data: DS4LanguageDataSourceData; data: DS4LanguageDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Language extends DS4Item {} export class DS4Language extends DS4Item {}

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4LootDataSourceData } from "./loot-data-source"; import type { DS4LootDataSourceData } from './loot-data-source';
export interface DS4LootDataProperties { export interface DS4LootDataProperties {
type: "loot"; type: 'loot';
data: DS4LootDataPropertiesData; data: DS4LootDataPropertiesData;
} }

View file

@ -2,10 +2,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataSourceDataBase, DS4ItemDataSourceDataPhysical } from "../item-data-source-base"; import type { DS4ItemDataSourceDataBase, DS4ItemDataSourceDataPhysical } from '../item-data-source-base';
export interface DS4LootDataSource { export interface DS4LootDataSource {
type: "loot"; type: 'loot';
data: DS4LootDataSourceData; data: DS4LootDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Loot extends DS4Item {} export class DS4Loot extends DS4Item {}

View file

@ -2,19 +2,19 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../../utils/utils"; import { getGame } from '../../utils/utils';
import { DS4Alphabet } from "./alphabet/alphabet"; import { DS4Alphabet } from './alphabet/alphabet';
import { DS4Armor } from "./armor/armor"; import { DS4Armor } from './armor/armor';
import { DS4Equipment } from "./equipment/equipment"; import { DS4Equipment } from './equipment/equipment';
import { DS4Item } from "./item"; import { DS4Item } from './item';
import { DS4Language } from "./language/language"; import { DS4Language } from './language/language';
import { DS4Loot } from "./loot/loot"; import { DS4Loot } from './loot/loot';
import { DS4RacialAbility } from "./racial-ability/racial-ability"; import { DS4RacialAbility } from './racial-ability/racial-ability';
import { DS4Shield } from "./shield/shield"; import { DS4Shield } from './shield/shield';
import { DS4SpecialCreatureAbility } from "./special-creature-ability/special-creature-ability"; import { DS4SpecialCreatureAbility } from './special-creature-ability/special-creature-ability';
import { DS4Spell } from "./spell/spell"; import { DS4Spell } from './spell/spell';
import { DS4Talent } from "./talent/talent"; import { DS4Talent } from './talent/talent';
import { DS4Weapon } from "./weapon/weapon"; import { DS4Weapon } from './weapon/weapon';
const handler = { const handler = {
/** /**
@ -23,30 +23,30 @@ const handler = {
*/ */
construct(_, args) { construct(_, args) {
switch (args[0]?.type) { switch (args[0]?.type) {
case "alphabet": case 'alphabet':
return new DS4Alphabet(...args); return new DS4Alphabet(...args);
case "armor": case 'armor':
return new DS4Armor(...args); return new DS4Armor(...args);
case "equipment": case 'equipment':
return new DS4Equipment(...args); return new DS4Equipment(...args);
case "language": case 'language':
return new DS4Language(...args); return new DS4Language(...args);
case "loot": case 'loot':
return new DS4Loot(...args); return new DS4Loot(...args);
case "racialAbility": case 'racialAbility':
return new DS4RacialAbility(...args); return new DS4RacialAbility(...args);
case "shield": case 'shield':
return new DS4Shield(...args); return new DS4Shield(...args);
case "specialCreatureAbility": case 'specialCreatureAbility':
return new DS4SpecialCreatureAbility(...args); return new DS4SpecialCreatureAbility(...args);
case "spell": case 'spell':
return new DS4Spell(...args); return new DS4Spell(...args);
case "talent": case 'talent':
return new DS4Talent(...args); return new DS4Talent(...args);
case "weapon": case 'weapon':
return new DS4Weapon(...args); return new DS4Weapon(...args);
default: default:
throw new Error(getGame().i18n.format("DS4.ErrorInvalidItemType", { type: args[0]?.type })); throw new Error(getGame().i18n.format('DS4.ErrorInvalidItemType', { type: args[0]?.type }));
} }
}, },
}; };

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4RacialAbilityDataSourceData } from "./racial-ability-data-source"; import type { DS4RacialAbilityDataSourceData } from './racial-ability-data-source';
export interface DS4RacialAbilityDataProperties { export interface DS4RacialAbilityDataProperties {
type: "racialAbility"; type: 'racialAbility';
data: DS4RacialAbilityDataPropertiesData; data: DS4RacialAbilityDataPropertiesData;
} }

View file

@ -2,10 +2,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataSourceDataBase } from "../item-data-source-base"; import type { DS4ItemDataSourceDataBase } from '../item-data-source-base';
export interface DS4RacialAbilityDataSource { export interface DS4RacialAbilityDataSource {
type: "racialAbility"; type: 'racialAbility';
data: DS4RacialAbilityDataSourceData; data: DS4RacialAbilityDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4RacialAbility extends DS4Item {} export class DS4RacialAbility extends DS4Item {}

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4ShieldDataSourceData } from "./shield-data-source"; import type { DS4ShieldDataSourceData } from './shield-data-source';
export interface DS4ShieldDataProperties { export interface DS4ShieldDataProperties {
type: "shield"; type: 'shield';
data: DS4ShieldDataPropertiesData; data: DS4ShieldDataPropertiesData;
} }

View file

@ -7,10 +7,10 @@ import type {
DS4ItemDataSourceDataEquipable, DS4ItemDataSourceDataEquipable,
DS4ItemDataSourceDataPhysical, DS4ItemDataSourceDataPhysical,
DS4ItemDataSourceDataProtective, DS4ItemDataSourceDataProtective,
} from "../item-data-source-base"; } from '../item-data-source-base';
export interface DS4ShieldDataSource { export interface DS4ShieldDataSource {
type: "shield"; type: 'shield';
data: DS4ShieldDataSourceData; data: DS4ShieldDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Shield extends DS4Item {} export class DS4Shield extends DS4Item {}

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4SpecialCreatureAbilityDataSourceData } from "./special-creature-ability-data-source"; import type { DS4SpecialCreatureAbilityDataSourceData } from './special-creature-ability-data-source';
export interface DS4SpecialCreatureAbilityDataProperties { export interface DS4SpecialCreatureAbilityDataProperties {
type: "specialCreatureAbility"; type: 'specialCreatureAbility';
data: DS4SpecialCreatureAbilityDataPropertiesData; data: DS4SpecialCreatureAbilityDataPropertiesData;
} }

View file

@ -2,10 +2,10 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataSourceDataBase } from "../item-data-source-base"; import type { DS4ItemDataSourceDataBase } from '../item-data-source-base';
export interface DS4SpecialCreatureAbilityDataSource { export interface DS4SpecialCreatureAbilityDataSource {
type: "specialCreatureAbility"; type: 'specialCreatureAbility';
data: DS4SpecialCreatureAbilityDataSourceData; data: DS4SpecialCreatureAbilityDataSourceData;
} }

View file

@ -2,6 +2,6 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4SpecialCreatureAbility extends DS4Item {} export class DS4SpecialCreatureAbility extends DS4Item {}

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { CooldownDuration, DS4SpellDataSourceData } from "./spell-data-source"; import type { CooldownDuration, DS4SpellDataSourceData } from './spell-data-source';
export function calculateSpellPrice(data: DS4SpellDataSourceData): number | null { export function calculateSpellPrice(data: DS4SpellDataSourceData): number | null {
const spellPriceFactor = calculateSpellPriceFactor(data.cooldownDuration); const spellPriceFactor = calculateSpellPriceFactor(data.cooldownDuration);
@ -17,16 +17,16 @@ export function calculateSpellPrice(data: DS4SpellDataSourceData): number | null
function calculateSpellPriceFactor(cooldownDuration: CooldownDuration): number { function calculateSpellPriceFactor(cooldownDuration: CooldownDuration): number {
switch (cooldownDuration) { switch (cooldownDuration) {
case "0r": case '0r':
case "1r": case '1r':
case "2r": case '2r':
case "5r": case '5r':
case "10r": case '10r':
case "100r": case '100r':
return 1; return 1;
case "1d": case '1d':
return 2; return 2;
case "d20d": case 'd20d':
return 3; return 3;
} }
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4SpellDataSourceData } from "./spell-data-source"; import type { DS4SpellDataSourceData } from './spell-data-source';
export interface DS4SpellDataProperties { export interface DS4SpellDataProperties {
type: "spell"; type: 'spell';
data: DS4SpellDataPropertiesData; data: DS4SpellDataPropertiesData;
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4 } from "../../../config"; import type { DS4 } from '../../../config';
import type { DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable } from "../item-data-source-base"; import type { DS4ItemDataSourceDataBase, DS4ItemDataSourceDataEquipable } from '../item-data-source-base';
export interface DS4SpellDataSource { export interface DS4SpellDataSource {
type: "spell"; type: 'spell';
data: DS4SpellDataSourceData; data: DS4SpellDataSourceData;
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { createCheckRoll } from "../../../dice/check-factory"; import { createCheckRoll } from '../../../dice/check-factory';
import { notifications } from "../../../ui/notifications"; import { notifications } from '../../../ui/notifications';
import { getGame } from "../../../utils/utils"; import { getGame } from '../../../utils/utils';
import { DS4Item } from "../item"; import { DS4Item } from '../item';
import { calculateSpellPrice } from "./calculate-spell-price"; import { calculateSpellPrice } from './calculate-spell-price';
export class DS4Spell extends DS4Item { export class DS4Spell extends DS4Item {
/** @override */ /** @override */
@ -24,7 +24,7 @@ export class DS4Spell extends DS4Item {
if (!this.system.equipped) { if (!this.system.equipped) {
return notifications.warn( return notifications.warn(
game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { game.i18n.format('DS4.WarningItemMustBeEquippedToBeRolled', {
name: this.name, name: this.name,
id: this.id, id: this.id,
type: this.type, type: this.type,
@ -33,14 +33,14 @@ export class DS4Spell extends DS4Item {
} }
if (!this.actor) { if (!this.actor) {
throw new Error(game.i18n.format("DS4.ErrorCannotRollUnownedItem", { name: this.name, id: this.id })); throw new Error(game.i18n.format('DS4.ErrorCannotRollUnownedItem', { name: this.name, id: this.id }));
} }
const ownerSystemData = this.actor.system; const ownerSystemData = this.actor.system;
const hasComplexModifier = this.system.spellModifier.complex !== ""; const hasComplexModifier = this.system.spellModifier.complex !== '';
if (hasComplexModifier === undefined) { if (hasComplexModifier === undefined) {
notifications.info( notifications.info(
game.i18n.format("DS4.InfoManuallyEnterSpellModifier", { game.i18n.format('DS4.InfoManuallyEnterSpellModifier', {
name: this.name, name: this.name,
spellModifier: this.system.spellModifier.complex, spellModifier: this.system.spellModifier.complex,
}), }),
@ -49,24 +49,23 @@ export class DS4Spell extends DS4Item {
const spellType = this.system.spellType; const spellType = this.system.spellType;
const opponentDefense = this.system.opponentDefense; const opponentDefense = this.system.opponentDefense;
const checkTargetNumber = const checkTargetNumber =
ownerSystemData.combatValues[spellType].total + ownerSystemData.combatValues[spellType].total + (hasComplexModifier ? 0 : this.system.spellModifier.numerical);
(hasComplexModifier ? 0 : this.system.spellModifier.numerical);
const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker });
const flavor = const flavor =
opponentDefense !== undefined && opponentDefense !== 0 opponentDefense !== undefined && opponentDefense !== 0
? "DS4.ItemSpellCheckFlavorWithOpponentDefense" ? 'DS4.ItemSpellCheckFlavorWithOpponentDefense'
: "DS4.ItemSpellCheckFlavor"; : 'DS4.ItemSpellCheckFlavor';
/** @type {import("../../../dice/check-factory").DS4CheckFactoryOptions["flavorData"]} */ /** @type {import("../../../dice/check-factory").DS4CheckFactoryOptions["flavorData"]} */
const flavorData = { const flavorData = {
actor: speaker.alias ?? this.actor.name, actor: speaker.alias ?? this.actor.name,
spell: this.name, spell: this.name,
}; };
if (opponentDefense !== undefined && opponentDefense !== 0) { if (opponentDefense !== undefined && opponentDefense !== 0) {
flavorData.opponentDefense = (opponentDefense < 0 ? "" : "+") + opponentDefense; flavorData.opponentDefense = (opponentDefense < 0 ? '' : '+') + opponentDefense;
} }
await createCheckRoll(checkTargetNumber, { await createCheckRoll(checkTargetNumber, {
rollMode: game.settings.get("core", "rollMode"), rollMode: game.settings.get('core', 'rollMode'),
maximumCoupResult: ownerSystemData.rolling.maximumCoupResult, maximumCoupResult: ownerSystemData.rolling.maximumCoupResult,
minimumFumbleResult: ownerSystemData.rolling.minimumFumbleResult, minimumFumbleResult: ownerSystemData.rolling.minimumFumbleResult,
flavor: flavor, flavor: flavor,
@ -80,6 +79,6 @@ export class DS4Spell extends DS4Item {
* @memberof hookEvents * @memberof hookEvents
* @param {DS4Item} item Item being rolled. * @param {DS4Item} item Item being rolled.
*/ */
Hooks.callAll("ds4.rollItem", this); Hooks.callAll('ds4.rollItem', this);
} }
} }

View file

@ -2,12 +2,12 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { ModifiableDataBaseTotalMax } from "../../common/common-data"; import type { ModifiableDataBaseTotalMax } from '../../common/common-data';
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4TalentDataSourceData } from "./talent-data-source"; import type { DS4TalentDataSourceData } from './talent-data-source';
export interface DS4TalentDataProperties { export interface DS4TalentDataProperties {
type: "talent"; type: 'talent';
data: DS4TalentDataPropertiesData; data: DS4TalentDataPropertiesData;
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { ModifiableDataBaseMax } from "../../common/common-data"; import type { ModifiableDataBaseMax } from '../../common/common-data';
import type { DS4ItemDataSourceDataBase } from "../item-data-source-base"; import type { DS4ItemDataSourceDataBase } from '../item-data-source-base';
export interface DS4TalentDataSource { export interface DS4TalentDataSource {
type: "talent"; type: 'talent';
data: DS4TalentDataSourceData; data: DS4TalentDataSourceData;
} }

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Talent extends DS4Item { export class DS4Talent extends DS4Item {
/** @override */ /** @override */

View file

@ -2,8 +2,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4ItemDataPropertiesDataRollable } from "../item-data-properties-base"; import type { DS4ItemDataPropertiesDataRollable } from '../item-data-properties-base';
import type { DS4WeaponDataSourceData } from "./weapon-data-source"; import type { DS4WeaponDataSourceData } from './weapon-data-source';
interface DS4WeaponDataPropertiesData extends DS4WeaponDataSourceData, DS4ItemDataPropertiesDataRollable { interface DS4WeaponDataPropertiesData extends DS4WeaponDataSourceData, DS4ItemDataPropertiesDataRollable {
opponentDefenseForAttackType: { opponentDefenseForAttackType: {
@ -13,6 +13,6 @@ interface DS4WeaponDataPropertiesData extends DS4WeaponDataSourceData, DS4ItemDa
} }
export interface DS4WeaponDataProperties { export interface DS4WeaponDataProperties {
type: "weapon"; type: 'weapon';
data: DS4WeaponDataPropertiesData; data: DS4WeaponDataPropertiesData;
} }

View file

@ -2,15 +2,15 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { DS4 } from "../../../config"; import type { DS4 } from '../../../config';
import type { import type {
DS4ItemDataSourceDataBase, DS4ItemDataSourceDataBase,
DS4ItemDataSourceDataEquipable, DS4ItemDataSourceDataEquipable,
DS4ItemDataSourceDataPhysical, DS4ItemDataSourceDataPhysical,
} from "../item-data-source-base"; } from '../item-data-source-base';
export interface DS4WeaponDataSource { export interface DS4WeaponDataSource {
type: "weapon"; type: 'weapon';
data: DS4WeaponDataSourceData; data: DS4WeaponDataSourceData;
} }

View file

@ -2,11 +2,11 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { DS4 } from "../../../config"; import { DS4 } from '../../../config';
import { createCheckRoll } from "../../../dice/check-factory"; import { createCheckRoll } from '../../../dice/check-factory';
import { notifications } from "../../../ui/notifications"; import { notifications } from '../../../ui/notifications';
import { getGame } from "../../../utils/utils"; import { getGame } from '../../../utils/utils';
import { DS4Item } from "../item"; import { DS4Item } from '../item';
export class DS4Weapon extends DS4Item { export class DS4Weapon extends DS4Item {
/** @override */ /** @override */
@ -14,10 +14,10 @@ export class DS4Weapon extends DS4Item {
const system = this.system; const system = this.system;
system.rollable = system.equipped; system.rollable = system.equipped;
system.opponentDefenseForAttackType = {}; system.opponentDefenseForAttackType = {};
if (system.attackType === "melee" || system.attackType === "meleeRanged") { if (system.attackType === 'melee' || system.attackType === 'meleeRanged') {
system.opponentDefenseForAttackType.melee = system.opponentDefense; system.opponentDefenseForAttackType.melee = system.opponentDefense;
} }
if (system.attackType === "ranged" || system.attackType === "meleeRanged") { if (system.attackType === 'ranged' || system.attackType === 'meleeRanged') {
system.opponentDefenseForAttackType.ranged = system.opponentDefense; system.opponentDefenseForAttackType.ranged = system.opponentDefense;
} }
} }
@ -27,7 +27,7 @@ export class DS4Weapon extends DS4Item {
const game = getGame(); const game = getGame();
if (!this.system.equipped) { if (!this.system.equipped) {
return notifications.warn( return notifications.warn(
game.i18n.format("DS4.WarningItemMustBeEquippedToBeRolled", { game.i18n.format('DS4.WarningItemMustBeEquippedToBeRolled', {
name: this.name, name: this.name,
id: this.id, id: this.id,
type: this.type, type: this.type,
@ -36,7 +36,7 @@ export class DS4Weapon extends DS4Item {
} }
if (!this.actor) { if (!this.actor) {
throw new Error(game.i18n.format("DS4.ErrorCannotRollUnownedItem", { name: this.name, id: this.id })); throw new Error(game.i18n.format('DS4.ErrorCannotRollUnownedItem', { name: this.name, id: this.id }));
} }
const ownerSystemData = this.actor.system; const ownerSystemData = this.actor.system;
@ -48,19 +48,19 @@ export class DS4Weapon extends DS4Item {
const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker }); const speaker = ChatMessage.getSpeaker({ actor: this.actor, ...options.speaker });
const flavor = const flavor =
opponentDefense !== undefined && opponentDefense !== 0 opponentDefense !== undefined && opponentDefense !== 0
? "DS4.ItemWeaponCheckFlavorWithOpponentDefense" ? 'DS4.ItemWeaponCheckFlavorWithOpponentDefense'
: "DS4.ItemWeaponCheckFlavor"; : 'DS4.ItemWeaponCheckFlavor';
/** @type {import("../../../dice/check-factory").DS4CheckFactoryOptions["flavorData"]} */ /** @type {import("../../../dice/check-factory").DS4CheckFactoryOptions["flavorData"]} */
const flavorData = { const flavorData = {
actor: speaker.alias ?? this.actor.name, actor: speaker.alias ?? this.actor.name,
weapon: this.name, weapon: this.name,
}; };
if (opponentDefense !== undefined && opponentDefense !== 0) { if (opponentDefense !== undefined && opponentDefense !== 0) {
flavorData.opponentDefense = (opponentDefense < 0 ? "" : "+") + opponentDefense; flavorData.opponentDefense = (opponentDefense < 0 ? '' : '+') + opponentDefense;
} }
await createCheckRoll(checkTargetNumber, { await createCheckRoll(checkTargetNumber, {
rollMode: getGame().settings.get("core", "rollMode"), rollMode: getGame().settings.get('core', 'rollMode'),
maximumCoupResult: ownerSystemData.rolling.maximumCoupResult, maximumCoupResult: ownerSystemData.rolling.maximumCoupResult,
minimumFumbleResult: ownerSystemData.rolling.minimumFumbleResult, minimumFumbleResult: ownerSystemData.rolling.minimumFumbleResult,
speaker, speaker,
@ -68,7 +68,7 @@ export class DS4Weapon extends DS4Item {
flavorData, flavorData,
}); });
Hooks.callAll("ds4.rollItem", this); Hooks.callAll('ds4.rollItem', this);
} }
/** /**
@ -77,29 +77,29 @@ export class DS4Weapon extends DS4Item {
* @protected * @protected
*/ */
async getPerformedAttackType() { async getPerformedAttackType() {
if (this.system.attackType !== "meleeRanged") { if (this.system.attackType !== 'meleeRanged') {
return this.system.attackType; return this.system.attackType;
} }
const { melee, ranged } = { ...DS4.i18n.attackTypes }; const { melee, ranged } = { ...DS4.i18n.attackTypes };
const identifier = `attack-type-selection-${foundry.utils.randomID()}`; const identifier = `attack-type-selection-${foundry.utils.randomID()}`;
return Dialog.prompt({ return Dialog.prompt({
title: getGame().i18n.localize("DS4.DialogAttackTypeSelection"), title: getGame().i18n.localize('DS4.DialogAttackTypeSelection'),
content: await renderTemplate("systems/ds4/templates/dialogs/simple-select-form.hbs", { content: await renderTemplate('systems/ds4/templates/dialogs/simple-select-form.hbs', {
selects: [ selects: [
{ {
label: getGame().i18n.localize("DS4.AttackType"), label: getGame().i18n.localize('DS4.AttackType'),
identifier, identifier,
options: { melee, ranged }, options: { melee, ranged },
}, },
], ],
}), }),
label: getGame().i18n.localize("DS4.GenericOkButton"), label: getGame().i18n.localize('DS4.GenericOkButton'),
callback: (html) => { callback: (html) => {
const selectedAttackType = html.find(`#${identifier}`).val(); const selectedAttackType = html.find(`#${identifier}`).val();
if (selectedAttackType !== "melee" && selectedAttackType !== "ranged") { if (selectedAttackType !== 'melee' && selectedAttackType !== 'ranged') {
throw new Error( throw new Error(
getGame().i18n.format("DS4.ErrorUnexpectedAttackType", { getGame().i18n.format('DS4.ErrorUnexpectedAttackType', {
actualType: selectedAttackType, actualType: selectedAttackType,
expectedTypes: "'melee', 'ranged'", expectedTypes: "'melee', 'ranged'",
}), }),

View file

@ -2,8 +2,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { getGame } from "../utils/utils"; import { getGame } from '../utils/utils';
import { DS4ActorProxy } from "./actor/proxy"; import { DS4ActorProxy } from './actor/proxy';
/** @type {object | undefined} */ /** @type {object | undefined} */
let fallbackData = undefined; let fallbackData = undefined;
@ -12,7 +12,7 @@ function getFallbackData() {
if (!fallbackData) { if (!fallbackData) {
fallbackData = {}; fallbackData = {};
for (const type of getGame().system.template.Actor?.types ?? []) { for (const type of getGame().system.template.Actor?.types ?? []) {
foundry.utils.mergeObject(fallbackData, new DS4ActorProxy({ type, name: "temporary" }).system); foundry.utils.mergeObject(fallbackData, new DS4ActorProxy({ type, name: 'temporary' }).system);
} }
} }
return fallbackData; return fallbackData;

View file

@ -2,8 +2,8 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import "../scss/ds4.scss"; import '../scss/ds4.scss';
import { registerForHooks } from "./hooks/hooks"; import { registerForHooks } from './hooks/hooks';
registerForHooks(); registerForHooks();

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import { Validator } from "./validator"; import { Validator } from './validator';
export class Evaluator<Context extends object> { export class Evaluator<Context extends object> {
context?: Context; context?: Context;
@ -28,7 +28,7 @@ export class Evaluator<Context extends object> {
this.validator.validate(expression); this.validator.validate(expression);
const body = `with (sandbox) { return ${expression}; }`; const body = `with (sandbox) { return ${expression}; }`;
const evaluate = new Function("sandbox", body); const evaluate = new Function('sandbox', body);
return evaluate(this.context ?? {}); return evaluate(this.context ?? {});
} }
} }

View file

@ -15,47 +15,47 @@ interface TokenWithoutSymbol {
pos: number; pos: number;
} }
type TypeWithSymbol = "iden" | "number" | "string"; type TypeWithSymbol = 'iden' | 'number' | 'string';
type TypeWithoutSymbol = type TypeWithoutSymbol =
| "+" | '+'
| "-" | '-'
| "*" | '*'
| "**" | '**'
| "/" | '/'
| "%" | '%'
| "===" | '==='
| "!==" | '!=='
| "==" | '=='
| "!=" | '!='
| "<" | '<'
| "<=" | '<='
| ">" | '>'
| ">=" | '>='
| "&&" | '&&'
| "||" | '||'
| "&" | '&'
| "|" | '|'
| "~" | '~'
| "^" | '^'
| "<<" | '<<'
| ">>" | '>>'
| ">>>" | '>>>'
| "." | '.'
| "?." | '?.'
| "??" | '??'
| "!" | '!'
| "?" | '?'
| ":" | ':'
| "(" | '('
| ")" | ')'
| "[" | '['
| "]" | ']'
| "," | ','
| "{" | '{'
| "}" | '}'
| "invalid" | 'invalid'
| "eof"; | 'eof';
export const literals = ["true", "false", "null", "undefined"]; export const literals = ['true', 'false', 'null', 'undefined'];
export const safeOperators = ["in", "instanceof", "typeof", "void"]; export const safeOperators = ['in', 'instanceof', 'typeof', 'void'];

View file

@ -2,7 +2,7 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
import type { Token } from "./grammar"; import type { Token } from './grammar';
export class Lexer { export class Lexer {
constructor(private readonly input: string) {} constructor(private readonly input: string) {}
@ -17,7 +17,7 @@ export class Lexer {
const [token, newPos] = this.getNextToken(pos); const [token, newPos] = this.getNextToken(pos);
pos = newPos; pos = newPos;
yield token; yield token;
if (token.type === "eof" || token.type === "invalid") { if (token.type === 'eof' || token.type === 'invalid') {
break; break;
} }
} }
@ -27,7 +27,7 @@ export class Lexer {
const current = this.input[pos]; const current = this.input[pos];
if (current === undefined) { if (current === undefined) {
return [{ type: "eof", pos }, pos]; return [{ type: 'eof', pos }, pos];
} }
if (this.isOperatorStart(current)) { if (this.isOperatorStart(current)) {
return this.getOperator(pos); return this.getOperator(pos);
@ -35,10 +35,10 @@ export class Lexer {
if (this.isDigit(current)) { if (this.isDigit(current)) {
return this.getNumber(pos); return this.getNumber(pos);
} }
if (current === "'" || current === '"' || current === "`") { if (current === "'" || current === '"' || current === '`') {
return this.getString(pos); return this.getString(pos);
} }
if (current === ".") { if (current === '.') {
const next = this.input[pos + 1]; const next = this.input[pos + 1];
if (this.isDigit(next)) { if (this.isDigit(next)) {
return this.getNumber(pos); return this.getNumber(pos);
@ -48,34 +48,34 @@ export class Lexer {
if (this.isIdentifierStart(current)) { if (this.isIdentifierStart(current)) {
return this.getIdentifier(pos); return this.getIdentifier(pos);
} }
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
private isOperatorStart(char: string) { private isOperatorStart(char: string) {
const operatorStartChars: (string | undefined)[] = [ const operatorStartChars: (string | undefined)[] = [
"+", '+',
"-", '-',
"*", '*',
"/", '/',
"%", '%',
"=", '=',
"!", '!',
">", '>',
"<", '<',
"&", '&',
"|", '|',
"~", '~',
"^", '^',
"?", '?',
":", ':',
"!", '!',
",", ',',
"(", '(',
")", ')',
"[", '[',
"]", ']',
"{", '{',
"}", '}',
]; ];
return operatorStartChars.includes(char[0]); return operatorStartChars.includes(char[0]);
} }
@ -85,107 +85,107 @@ export class Lexer {
const next = this.input[pos + 1]; const next = this.input[pos + 1];
const nextButOne = this.input[pos + 2]; const nextButOne = this.input[pos + 2];
switch (current) { switch (current) {
case "+": case '+':
case "-": case '-':
case "/": case '/':
case "%": case '%':
case "~": case '~':
case "^": case '^':
case ".": case '.':
case ":": case ':':
case ",": case ',':
case "(": case '(':
case ")": case ')':
case "[": case '[':
case "]": case ']':
case "{": case '{':
case "}": { case '}': {
return [{ type: current, pos }, pos + 1]; return [{ type: current, pos }, pos + 1];
} }
case "*": { case '*': {
if (next === "*") { if (next === '*') {
return [{ type: "**", pos }, pos + 2]; return [{ type: '**', pos }, pos + 2];
} }
return [{ type: "*", pos }, pos + 1]; return [{ type: '*', pos }, pos + 1];
} }
case "=": { case '=': {
if (next === "=") { if (next === '=') {
if (nextButOne === "=") { if (nextButOne === '=') {
return [{ type: "===", pos }, pos + 3]; return [{ type: '===', pos }, pos + 3];
} }
return [{ type: "==", pos }, pos + 2]; return [{ type: '==', pos }, pos + 2];
} }
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
case "!": { case '!': {
if (next === "=") { if (next === '=') {
if (nextButOne === "=") { if (nextButOne === '=') {
return [{ type: "!==", pos }, pos + 3]; return [{ type: '!==', pos }, pos + 3];
} }
return [{ type: "!=", pos }, pos + 2]; return [{ type: '!=', pos }, pos + 2];
} }
return [{ type: "!", pos }, pos + 1]; return [{ type: '!', pos }, pos + 1];
} }
case ">": { case '>': {
switch (next) { switch (next) {
case ">": { case '>': {
if (nextButOne === ">") { if (nextButOne === '>') {
return [{ type: ">>>", pos }, pos + 3]; return [{ type: '>>>', pos }, pos + 3];
} }
return [{ type: ">>", pos }, pos + 2]; return [{ type: '>>', pos }, pos + 2];
} }
case "=": { case '=': {
return [{ type: ">=", pos }, pos + 2]; return [{ type: '>=', pos }, pos + 2];
} }
default: { default: {
return [{ type: ">", pos }, pos + 1]; return [{ type: '>', pos }, pos + 1];
} }
} }
} }
case "<": { case '<': {
switch (next) { switch (next) {
case "=": { case '=': {
return [{ type: "<=", pos }, pos + 2]; return [{ type: '<=', pos }, pos + 2];
} }
case "<": { case '<': {
return [{ type: "<<", pos }, pos + 2]; return [{ type: '<<', pos }, pos + 2];
} }
default: { default: {
return [{ type: "<", pos }, pos + 1]; return [{ type: '<', pos }, pos + 1];
} }
} }
} }
case "&": { case '&': {
if (next === "&") { if (next === '&') {
return [{ type: "&&", pos }, pos + 2]; return [{ type: '&&', pos }, pos + 2];
} }
return [{ type: "&", pos }, pos + 1]; return [{ type: '&', pos }, pos + 1];
} }
case "|": { case '|': {
if (next === "|") { if (next === '|') {
return [{ type: "||", pos }, pos + 2]; return [{ type: '||', pos }, pos + 2];
} }
return [{ type: "|", pos }, pos + 1]; return [{ type: '|', pos }, pos + 1];
} }
case "?": { case '?': {
switch (next) { switch (next) {
case ".": { case '.': {
return [{ type: "?.", pos }, pos + 2]; return [{ type: '?.', pos }, pos + 2];
} }
case "?": { case '?': {
return [{ type: "??", pos }, pos + 2]; return [{ type: '??', pos }, pos + 2];
} }
default: { default: {
return [{ type: "?", pos }, pos + 1]; return [{ type: '?', pos }, pos + 1];
} }
} }
} }
} }
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
private isDigit(char: string | undefined): char is `${number}` { private isDigit(char: string | undefined): char is `${number}` {
return /\d/.test(char?.[0] ?? ""); return /\d/.test(char?.[0] ?? '');
} }
private getNumber(pos: number): [Token, number] { private getNumber(pos: number): [Token, number] {
@ -194,43 +194,40 @@ export class Lexer {
let only0s = false; let only0s = false;
while ( while (
this.isDigit(this.input[endPos]) || this.isDigit(this.input[endPos]) ||
this.input[endPos] === "." || this.input[endPos] === '.' ||
(this.input[endPos] === "_" && endPos > pos) (this.input[endPos] === '_' && endPos > pos)
) { ) {
if (this.input[endPos] === ".") { if (this.input[endPos] === '.') {
if (foundDot) { if (foundDot) {
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
foundDot = true; foundDot = true;
} }
if (this.input[endPos] === "0") { if (this.input[endPos] === '0') {
only0s = endPos === pos ? true : only0s; only0s = endPos === pos ? true : only0s;
} }
if ( if (this.input[endPos] === '_' && (this.input[endPos - 1] === '_' || this.input[endPos - 1] === '.' || only0s)) {
this.input[endPos] === "_" && return [{ type: 'invalid', pos }, pos];
(this.input[endPos - 1] === "_" || this.input[endPos - 1] === "." || only0s)
) {
return [{ type: "invalid", pos }, pos];
} }
endPos += 1; endPos += 1;
} }
if (pos === endPos) { if (pos === endPos) {
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
if (this.input[endPos - 1] === "_") { if (this.input[endPos - 1] === '_') {
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
return [{ type: "number", symbol: this.input.slice(pos, endPos), pos }, endPos]; return [{ type: 'number', symbol: this.input.slice(pos, endPos), pos }, endPos];
} }
private isIdentifierStart(char: string | undefined) { private isIdentifierStart(char: string | undefined) {
return /[$_\p{ID_Start}]/u.test(char?.[0] ?? ""); return /[$_\p{ID_Start}]/u.test(char?.[0] ?? '');
} }
private isIdentifier(char: string | undefined) { private isIdentifier(char: string | undefined) {
return /[$\u200c\u200d\p{ID_Continue}]/u.test(char?.[0] ?? ""); return /[$\u200c\u200d\p{ID_Continue}]/u.test(char?.[0] ?? '');
} }
private getIdentifier(pos: number): [Token, number] { private getIdentifier(pos: number): [Token, number] {
@ -239,26 +236,26 @@ export class Lexer {
endPos += 1; endPos += 1;
} }
if (endPos === pos) { if (endPos === pos) {
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
return [{ type: "iden", symbol: this.input.slice(pos, endPos), pos }, endPos]; return [{ type: 'iden', symbol: this.input.slice(pos, endPos), pos }, endPos];
} }
private getString(pos: number): [Token, number] { private getString(pos: number): [Token, number] {
const quote = this.input[pos]; const quote = this.input[pos];
let endPos = pos + 1; let endPos = pos + 1;
let prev = this.input[pos]; let prev = this.input[pos];
while (endPos < this.input.length && (this.input[endPos] !== quote || prev === "\\")) { while (endPos < this.input.length && (this.input[endPos] !== quote || prev === '\\')) {
prev = this.input[endPos]; prev = this.input[endPos];
endPos += 1; endPos += 1;
} }
if (endPos === pos || this.input[endPos] !== quote) { if (endPos === pos || this.input[endPos] !== quote) {
return [{ type: "invalid", pos }, pos]; return [{ type: 'invalid', pos }, pos];
} }
return [{ type: "string", symbol: this.input.slice(pos, endPos + 1), pos }, endPos + 1]; return [{ type: 'string', symbol: this.input.slice(pos, endPos + 1), pos }, endPos + 1];
} }
private isWhiteSpace(char: string | undefined) { private isWhiteSpace(char: string | undefined) {
return /\s/.test(char?.[0] ?? ""); return /\s/.test(char?.[0] ?? '');
} }
} }

Some files were not shown because too many files have changed in this diff Show more