Merge branch 'master' into 012_equipped_flag
3
.gitignore
vendored
|
@ -14,3 +14,6 @@ dist
|
|||
|
||||
# ESLint
|
||||
.eslintcache
|
||||
|
||||
# Junit results
|
||||
results.xml
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
image: node:latest
|
||||
|
||||
stages:
|
||||
- mytest
|
||||
- prepare
|
||||
- test
|
||||
- build
|
||||
- deploy
|
||||
|
@ -12,29 +12,37 @@ cache: &global_cache
|
|||
- package-lock.json
|
||||
policy: pull
|
||||
paths:
|
||||
- .npm/
|
||||
- node_modules/
|
||||
|
||||
lint:
|
||||
stage: test
|
||||
before_script:
|
||||
- npm ci --cache .npm --prefer-offline
|
||||
install-dependencies:
|
||||
stage: prepare
|
||||
script:
|
||||
- npm run lint
|
||||
- npm install
|
||||
cache:
|
||||
<<: *global_cache
|
||||
policy: pull-push
|
||||
|
||||
# Test:
|
||||
# stage: Test
|
||||
# script:
|
||||
# - npm test
|
||||
# cache:
|
||||
# <<: *global_cache
|
||||
lint:
|
||||
stage: test
|
||||
script:
|
||||
- npm run lint
|
||||
cache:
|
||||
<<: *global_cache
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- npm run test:ci
|
||||
cache:
|
||||
<<: *global_cache
|
||||
artifacts:
|
||||
when: always
|
||||
reports:
|
||||
junit:
|
||||
- results.xml
|
||||
|
||||
build:
|
||||
stage: build
|
||||
before_script:
|
||||
- npm ci --cache .npm --prefer-offline
|
||||
script:
|
||||
- npm run build
|
||||
cache:
|
||||
|
|
8
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"gruntfuggly.todo-tree",
|
||||
"eg2.vscode-npm-script"
|
||||
]
|
||||
}
|
111
package-lock.json
generated
|
@ -110,6 +110,12 @@
|
|||
"fastq": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"@types/jasmine": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.6.2.tgz",
|
||||
"integrity": "sha512-AzfesNFLvOs6Q1mHzIsVJXSeUnqVh4ZHG8ngygKJfbkcSLwzrBVm/LKa+mR8KrOfnWtUL47112gde1MC0IXqpQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/jquery": {
|
||||
"version": "3.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz",
|
||||
|
@ -610,6 +616,12 @@
|
|||
"readable-stream": "^2.0.6"
|
||||
}
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
|
@ -1571,6 +1583,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"dev": true
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
|
||||
|
@ -1733,6 +1751,12 @@
|
|||
"integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"dev": true
|
||||
},
|
||||
"dir-glob": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
|
||||
|
@ -2678,7 +2702,7 @@
|
|||
}
|
||||
},
|
||||
"foundry-pc-types": {
|
||||
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#4ae5653e74e79bb6b4bd23b094dee066a0c7723a",
|
||||
"version": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#4e20e5c9cb1b3cd2e44555d7acfa89a3cf63f6ce",
|
||||
"from": "git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
@ -3748,6 +3772,41 @@
|
|||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
|
||||
"dev": true
|
||||
},
|
||||
"jasmine": {
|
||||
"version": "3.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.6.3.tgz",
|
||||
"integrity": "sha512-Th91zHsbsALWjDUIiU5d/W5zaYQsZFMPTdeNmi8GivZPmAaUAK8MblSG3yQI4VMGC/abF2us7ex60NH1AAIMTA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.6",
|
||||
"jasmine-core": "~3.6.0"
|
||||
}
|
||||
},
|
||||
"jasmine-core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz",
|
||||
"integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==",
|
||||
"dev": true
|
||||
},
|
||||
"jasmine-reporters": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-reporters/-/jasmine-reporters-2.3.2.tgz",
|
||||
"integrity": "sha512-u/7AT9SkuZsUfFBLLzbErohTGNsEUCKaQbsVYnLFW1gEuL2DzmBL4n8v90uZsqIqlWvWUgian8J6yOt5Fyk/+A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"xmldom": "^0.1.22"
|
||||
}
|
||||
},
|
||||
"jasmine-xml-reporter": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/jasmine-xml-reporter/-/jasmine-xml-reporter-1.2.1.tgz",
|
||||
"integrity": "sha1-fKoqUYAv7+2+JLPqinrUJ8nqfbM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jasmine-reporters": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"js-base64": {
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
|
||||
|
@ -4326,6 +4385,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"dev": true
|
||||
},
|
||||
"make-iterator": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
|
||||
|
@ -5993,6 +6058,24 @@
|
|||
"urix": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
|
||||
"integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"source-map-url": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
|
||||
|
@ -6514,6 +6597,20 @@
|
|||
"glob": "^7.1.2"
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz",
|
||||
"integrity": "sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.17",
|
||||
"yn": "3.1.1"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
|
@ -6949,6 +7046,12 @@
|
|||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
},
|
||||
"xmldom": {
|
||||
"version": "0.1.31",
|
||||
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz",
|
||||
"integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==",
|
||||
"dev": true
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
@ -7102,6 +7205,12 @@
|
|||
"object.assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
},
|
||||
"zip-stream": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.0.4.tgz",
|
||||
|
|
12
package.json
|
@ -11,11 +11,15 @@
|
|||
"clean": "gulp clean && gulp link --clean",
|
||||
"update": "npm install --save-dev git+https://git.f3l.de/dungeonslayers/foundry-pc-types.git#f3l-fixes",
|
||||
"lint": "eslint 'src/**/*.ts' --cache",
|
||||
"lint:fix": "eslint 'src/**/*.ts' --cache --fix"
|
||||
"lint:fix": "eslint 'src/**/*.ts' --cache --fix",
|
||||
"test": "ts-node ./node_modules/jasmine/bin/jasmine",
|
||||
"test:ci": "ts-node ./node_modules/jasmine-xml-reporter/bin/jasmine --junitreport",
|
||||
"format": "prettier --write 'src/**/*.(ts|json|scss)'"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "^3.6.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.11.0",
|
||||
"@typescript-eslint/parser": "^4.11.0",
|
||||
"archiver": "^5.1.0",
|
||||
|
@ -31,10 +35,13 @@
|
|||
"gulp-sass": "^4.1.0",
|
||||
"gulp-typescript": "^6.0.0-alpha.1",
|
||||
"husky": "^4.3.6",
|
||||
"jasmine": "^3.6.3",
|
||||
"jasmine-xml-reporter": "^1.2.1",
|
||||
"json-stringify-pretty-compact": "^2.0.0",
|
||||
"lint-staged": "^10.5.3",
|
||||
"prettier": "^2.2.1",
|
||||
"sass": "^1.30.0",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.1.3",
|
||||
"yargs": "^16.2.0"
|
||||
},
|
||||
|
@ -44,6 +51,7 @@
|
|||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.ts": "eslint --cache --fix"
|
||||
"*.ts": "eslint --cache --fix",
|
||||
"*.(json|scss)": "prettier --write"
|
||||
}
|
||||
}
|
||||
|
|
272
spec/support/ds4rolls.spec.ts
Normal file
|
@ -0,0 +1,272 @@
|
|||
import {
|
||||
rollCheckMultipleDice,
|
||||
rollCheckSingleDie,
|
||||
RollOptions,
|
||||
RollResult,
|
||||
RollResultStatus,
|
||||
} from "../../src/module/rolls/roll-executor";
|
||||
import { RollProvider } from "../../src/module/rolls/roll-provider";
|
||||
|
||||
import "jasmine";
|
||||
|
||||
describe("DS4 Rolls with one die and no modifications.", () => {
|
||||
it("Should do a regular success roll.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4);
|
||||
|
||||
expect(rollCheckSingleDie(12, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(4, RollResultStatus.SUCCESS, [4]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a single success roll on success upper edge case.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(4);
|
||||
|
||||
expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(4, RollResultStatus.SUCCESS, [4]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a single failure roll on lower edge case.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(5);
|
||||
|
||||
expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.FAILURE, [5]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a single failure roll on upper edge case '19'.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(19);
|
||||
|
||||
expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.FAILURE, [19]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a single crit success roll on '1'.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(1);
|
||||
|
||||
expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a single crit failure roll on '20'.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(20);
|
||||
|
||||
expect(rollCheckSingleDie(4, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DS4 Rolls with one die and crit roll modifications.", () => {
|
||||
it("Should do a crit success on `1`.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(1);
|
||||
|
||||
expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual(
|
||||
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [1]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a crit success on `maxCritSucc`.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(2);
|
||||
|
||||
expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual(
|
||||
new RollResult(4, RollResultStatus.CRITICAL_SUCCESS, [2]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a success on lower edge case `3`.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(3);
|
||||
|
||||
expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual(
|
||||
new RollResult(3, RollResultStatus.SUCCESS, [3]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a success on upper edge case `18`.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(18);
|
||||
|
||||
expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.FAILURE, [18]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a crit fail on `minCritFail`.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(19);
|
||||
|
||||
expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should do a crit fail on `20`", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRoll"]);
|
||||
|
||||
rollProvider.getNextRoll = jasmine.createSpy("getNextRoll").and.returnValue(20);
|
||||
|
||||
expect(rollCheckSingleDie(4, { maxCritSucc: 2, minCritFail: 19 }, rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DS4 Rools with multiple dice and no modifiers.", () => {
|
||||
it("Should do a crit fail on `20` for first roll.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([20, 15, 6]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [20, 15, 6]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with all rolls crit successes.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([1, 1, 1]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(48, RollResultStatus.SUCCESS, [1, 1, 1]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with the last roll not being suficient.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 15]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(30, RollResultStatus.SUCCESS, [15, 15, 15]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with the last roll a crit success.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 1]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(35, RollResultStatus.SUCCESS, [1, 15, 15]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with the last roll being 20 and one crit success.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 1, 20]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, new RollOptions(), rollProvider)).toEqual(
|
||||
new RollResult(40, RollResultStatus.SUCCESS, [1, 20, 15]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DS4 Rools with multiple dice and min/max modifiers.", () => {
|
||||
it("Should do a crit fail on `19` for first roll.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([19, 15, 6]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with all rolls crit successes (1 and 2).", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([2, 1, 2]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(48, RollResultStatus.SUCCESS, [2, 2, 1]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with the last roll not being suficient.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 15]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(30, RollResultStatus.SUCCESS, [15, 15, 15]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with the last roll a crit success `2`.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 15, 2]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(35, RollResultStatus.SUCCESS, [2, 15, 15]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with the last roll being `20` and one crit success '2'.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 2, 20]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(40, RollResultStatus.SUCCESS, [2, 20, 15]),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should succeed with the last roll being `19` and one crit success '2'.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([15, 2, 19]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { maxCritSucc: 2, minCritFail: 19 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(39, RollResultStatus.SUCCESS, [2, 19, 15]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DS4 Rools with multiple dice and fail modifiers.", () => {
|
||||
it("Should do a crit fail on `19` for first roll.", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([19, 15, 6]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { minCritFail: 19 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(0, RollResultStatus.CRITICAL_FAILURE, [19, 15, 6]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("DS4 Rools with multiple dice and success modifiers.", () => {
|
||||
it("Should succeed with all rolls crit successes (1 and 2).", () => {
|
||||
const rollProvider: RollProvider = jasmine.createSpyObj("rollProvider", ["getNextRolls"]);
|
||||
|
||||
rollProvider.getNextRolls = jasmine.createSpy("getNextRolls").and.returnValue([2, 1, 2]);
|
||||
|
||||
expect(rollCheckMultipleDice(48, { maxCritSucc: 2 } as RollOptions, rollProvider)).toEqual(
|
||||
new RollResult(48, RollResultStatus.SUCCESS, [2, 2, 1]),
|
||||
);
|
||||
});
|
||||
});
|
7
spec/support/jasmine.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"spec_dir": "spec",
|
||||
"spec_files": ["**/*[sS]pec.ts"],
|
||||
"helpers": ["helpers/**/*.ts"],
|
||||
"stopSpecOnExpectationFailure": false,
|
||||
"random": true
|
||||
}
|
BIN
src/assets/DS4-DEF.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
src/assets/DS4-HP.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
src/assets/DS4-INI.png
Normal file
After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 45 KiB |
BIN
src/assets/DS4-MR.png
Normal file
After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 36 KiB |
BIN
src/assets/DS4-MSC.png
Normal file
After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 48 KiB |
BIN
src/assets/DS4-SPC.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
src/assets/DS4-TSC.png
Normal file
After Width: | Height: | Size: 87 KiB |
|
@ -17,4 +17,5 @@
|
|||
@import "scss/components/tabs";
|
||||
@import "scss/components/items";
|
||||
@import "scss/components/description";
|
||||
@import "scss/components/character_values";
|
||||
}
|
||||
|
|
|
@ -56,5 +56,30 @@
|
|||
"DS4.ArmorMaterialTypeChain": "Chain",
|
||||
"DS4.ArmorMaterialTypeChainAbbr": "Chain",
|
||||
"DS4.ArmorMaterialTypePlate": "Plate",
|
||||
"DS4.ArmorMaterialTypePlateAbbr": "Plate"
|
||||
"DS4.ArmorMaterialTypePlateAbbr": "Plate",
|
||||
"DS4.AttributeBody": "Body",
|
||||
"DS4.AttributeMobility": "Mobility",
|
||||
"DS4.AttributeMind": "Mind",
|
||||
"DS4.TraitStrength": "Strength",
|
||||
"DS4.TraitConstitution": "Constitution",
|
||||
"DS4.TraitAgility": "Agility",
|
||||
"DS4.TraitDexterity": "Dexterity",
|
||||
"DS4.TraitIntellect": "Intellect",
|
||||
"DS4.TraitAura": "Aura",
|
||||
"DS4.CombatValuesHitPoints": "Hit Points",
|
||||
"DS4.CombatValuesDefense": "Defense",
|
||||
"DS4.CombatValuesInitiative": "Initiative",
|
||||
"DS4.CombatValuesMovement": "Movement",
|
||||
"DS4.CombatValuesMeleeAttack": "Melee Attack",
|
||||
"DS4.CombatValuesRangedAttack": "Ranged Attack",
|
||||
"DS4.CombatValuesSpellcasting": "Spellcasting",
|
||||
"DS4.CombatValuesTargetedSpellcasting": "Targeted Spellcasting",
|
||||
"DS4.BaseInfoRace": "Race",
|
||||
"DS4.BaseInfoClass": "Class",
|
||||
"DS4.BaseInfoHeroClass": "Hero Class",
|
||||
"DS4.BaseInfoRacialAbilities": "Racial Abilites",
|
||||
"DS4.ProgressionLevel": "Level",
|
||||
"DS4.ProgressionExperiencePoints": "Experience Points",
|
||||
"DS4.ProgressionTalentPoints": "Talent Points",
|
||||
"DS4.ProgressionProgressPoints": "Progress Points"
|
||||
}
|
||||
|
|
|
@ -1,26 +1,65 @@
|
|||
export interface DS4ActorDataType {
|
||||
attributes: DS4ActorDataAttributes;
|
||||
traits: DS4ActorDataTraits;
|
||||
combatValues: DS4ActorDataCombatValues;
|
||||
baseInfo: DS4ActorDataBaseInfo;
|
||||
progression: DS4ActorDataProgression;
|
||||
}
|
||||
|
||||
interface DS4ActorDataAttributes {
|
||||
body: BodyAttribute;
|
||||
mobility: ExtensibleData<number>;
|
||||
mind: ExtensibleData<number>;
|
||||
mobility: ModifiableData<number>;
|
||||
mind: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface ExtensibleData<T> {
|
||||
initial: T;
|
||||
export interface ModifiableData<T> {
|
||||
base: T;
|
||||
mod: T;
|
||||
total?: T;
|
||||
}
|
||||
|
||||
interface UsableResource<T> {
|
||||
total: T;
|
||||
used: T;
|
||||
}
|
||||
|
||||
interface CurrentData<T> extends ModifiableData<T> {
|
||||
current: T;
|
||||
}
|
||||
|
||||
// Blueprint in case we need more detailed differentiation
|
||||
type BodyAttribute = ExtensibleData<number>;
|
||||
type BodyAttribute = ModifiableData<number>;
|
||||
|
||||
interface DS4ActorDataTraits {
|
||||
strength: ExtensibleData<number>;
|
||||
constitution: ExtensibleData<number>;
|
||||
agility: ExtensibleData<number>;
|
||||
dexterity: ExtensibleData<number>;
|
||||
intellect: ExtensibleData<number>;
|
||||
aura: ExtensibleData<number>;
|
||||
strength: ModifiableData<number>;
|
||||
constitution: ModifiableData<number>;
|
||||
agility: ModifiableData<number>;
|
||||
dexterity: ModifiableData<number>;
|
||||
intellect: ModifiableData<number>;
|
||||
aura: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataCombatValues {
|
||||
hitPoints: CurrentData<number>;
|
||||
defense: ModifiableData<number>;
|
||||
initiative: ModifiableData<number>;
|
||||
movement: ModifiableData<number>;
|
||||
meleeAttack: ModifiableData<number>;
|
||||
rangedAttack: ModifiableData<number>;
|
||||
spellcasting: ModifiableData<number>;
|
||||
targetedSpellcasting: ModifiableData<number>;
|
||||
}
|
||||
|
||||
interface DS4ActorDataBaseInfo {
|
||||
race: string;
|
||||
class: string;
|
||||
heroClass: string;
|
||||
racialAbilities: string;
|
||||
}
|
||||
|
||||
interface DS4ActorDataProgression {
|
||||
level: number;
|
||||
experiencePoints: number;
|
||||
talentPoints: UsableResource<number>;
|
||||
progressPoints: UsableResource<number>;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ export class DS4ActorSheet extends ActorSheet<DS4ActorDataType, DS4Actor, DS4Ite
|
|||
return mergeObject(super.defaultOptions, {
|
||||
classes: ["ds4", "sheet", "actor"],
|
||||
template: "systems/ds4/templates/actor/actor-sheet.hbs",
|
||||
width: 600,
|
||||
width: 725,
|
||||
height: 600,
|
||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||
});
|
||||
|
|
|
@ -1,43 +1,22 @@
|
|||
import { DS4Item } from "../item/item";
|
||||
import { DS4ItemDataType } from "../item/item-data";
|
||||
import { DS4ActorDataType } from "./actor-data";
|
||||
import { DS4ActorDataType, ModifiableData } from "./actor-data";
|
||||
|
||||
/**
|
||||
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
|
||||
* @extends {Actor}
|
||||
*/
|
||||
export class DS4Actor extends Actor<DS4ActorDataType, DS4ItemDataType, DS4Item> {
|
||||
/** @override */
|
||||
prepareDerivedData(): void {
|
||||
const data = this.data;
|
||||
this._prepareCombatValues(data);
|
||||
}
|
||||
|
||||
private _prepareCombatValues(data: ActorData<DS4ActorDataType>): void {
|
||||
const hitPointsModifier = getProperty(data, "data.combatValues.hitPoints.modifier") || 0;
|
||||
const actorData = data.data;
|
||||
setProperty(
|
||||
data,
|
||||
"data.combatValues.hitPoints.max",
|
||||
actorData.attributes.body.initial + actorData.traits.constitution.initial + 10 + hitPointsModifier,
|
||||
const attributes = data.data.attributes;
|
||||
Object.values(attributes).forEach(
|
||||
(attribute: ModifiableData<number>) => (attribute.total = attribute.base + attribute.mod),
|
||||
);
|
||||
|
||||
const defenseModifier = getProperty(data, "data.combatValues.defense.modifier") || 0;
|
||||
setProperty(
|
||||
data,
|
||||
"data.combatValues.defense.value",
|
||||
actorData.attributes.body.initial +
|
||||
actorData.traits.constitution.initial +
|
||||
this._getArmorValue() +
|
||||
defenseModifier,
|
||||
);
|
||||
}
|
||||
const traits = data.data.traits;
|
||||
Object.values(traits).forEach((trait: ModifiableData<number>) => (trait.total = trait.base + trait.mod));
|
||||
|
||||
private _getArmorValue(): number {
|
||||
return this.data["items"]
|
||||
.filter((item) => ["armor", "shield"].includes(item.type))
|
||||
.filter((item) => item.data.equipped)
|
||||
.map((item) => item.data.armorValue)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
const combatValues = data.data.combatValues;
|
||||
Object.values(combatValues).forEach(
|
||||
(combatValue: ModifiableData<number>) => (combatValue.total = combatValue.base + combatValue.mod),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ export const DS4 = {
|
|||
|
||||
/**
|
||||
* Define the set of acttack types that can be performed with weapon items
|
||||
* @type {Object}
|
||||
*/
|
||||
attackTypes: {
|
||||
melee: "DS4.AttackTypeMelee",
|
||||
|
@ -19,8 +18,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the file paths to icon images
|
||||
* @type {Object}
|
||||
* Define the file paths to icon images
|
||||
*/
|
||||
attackTypesIcons: {
|
||||
melee: "systems/ds4/assets/DS4-MAT.png",
|
||||
|
@ -30,7 +28,6 @@ export const DS4 = {
|
|||
|
||||
/**
|
||||
* Define the set of item availabilties
|
||||
* @type {Object}
|
||||
*/
|
||||
itemAvailabilities: {
|
||||
unset: "DS4.ItemAvailabilityUnset",
|
||||
|
@ -43,8 +40,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the set of item types
|
||||
* @type {Object}
|
||||
* Define the set of item types
|
||||
*/
|
||||
itemTypes: {
|
||||
weapon: "DS4.ItemTypeWeapon",
|
||||
|
@ -55,8 +51,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the set of armor types, a character may only wear one item of each at any given time
|
||||
* @type {Object}
|
||||
* Define the set of armor types, a character may only wear one item of each at any given time
|
||||
*/
|
||||
armorTypes: {
|
||||
body: "DS4.ArmorTypeBody",
|
||||
|
@ -67,8 +62,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define abbreviations for the armor types
|
||||
* @type {Object}
|
||||
* Define abbreviations for the armor types
|
||||
*/
|
||||
armorTypesAbbr: {
|
||||
body: "DS4.ArmorTypeBodyAbbr",
|
||||
|
@ -79,8 +73,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
|
||||
* @type {Object}
|
||||
* Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
|
||||
*/
|
||||
armorMaterialTypes: {
|
||||
cloth: "DS4.ArmorMaterialTypeCloth",
|
||||
|
@ -90,8 +83,7 @@ export const DS4 = {
|
|||
},
|
||||
|
||||
/**
|
||||
* * Define the abbreviations of armor materials
|
||||
* @type {Object}
|
||||
* Define the abbreviations of armor materials
|
||||
*/
|
||||
armorMaterialTypesAbbr: {
|
||||
cloth: "DS4.ArmorMaterialTypeClothAbbr",
|
||||
|
@ -99,4 +91,59 @@ export const DS4 = {
|
|||
chain: "DS4.ArmorMaterialTypeChainAbbr",
|
||||
plate: "DS4.ArmorMaterialTypePlateAbbr",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the set of attributes a character has
|
||||
*/
|
||||
attributes: {
|
||||
body: "DS4.AttributeBody",
|
||||
mobility: "DS4.AttributeMobility",
|
||||
mind: "DS4.AttributeMind",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the set of traits a character has
|
||||
*/
|
||||
traits: {
|
||||
strength: "DS4.TraitStrength",
|
||||
constitution: "DS4.TraitConstitution",
|
||||
agility: "DS4.TraitAgility",
|
||||
dexterity: "DS4.TraitDexterity",
|
||||
intellect: "DS4.TraitIntellect",
|
||||
aura: "DS4.TraitAura",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the set of combat values a character has
|
||||
*/
|
||||
combatValues: {
|
||||
hitPoints: "DS4.CombatValuesHitPoints",
|
||||
defense: "DS4.CombatValuesDefense",
|
||||
initiative: "DS4.CombatValuesInitiative",
|
||||
movement: "DS4.CombatValuesMovement",
|
||||
meleeAttack: "DS4.CombatValuesMeleeAttack",
|
||||
rangedAttack: "DS4.CombatValuesRangedAttack",
|
||||
spellcasting: "DS4.CombatValuesSpellcasting",
|
||||
targetedSpellcasting: "DS4.CombatValuesTargetedSpellcasting",
|
||||
},
|
||||
|
||||
/**
|
||||
* Define the base info of a character
|
||||
*/
|
||||
baseInfo: {
|
||||
race: "DS4.BaseInfoRace",
|
||||
class: "DS4.BaseInfoClass",
|
||||
heroClass: "DS4.BaseInfoHeroClass",
|
||||
racialAbilities: "DS4.BaseInfoRacialAbilities",
|
||||
},
|
||||
|
||||
/**
|
||||
* Definme the progression info of a character
|
||||
*/
|
||||
progression: {
|
||||
level: "DS4.ProgressionLevel",
|
||||
experiencePoints: "DS4.ProgressionExperiencePoints",
|
||||
talentPoints: "DS4.ProgressionTalentPoints",
|
||||
progressPoints: "DS4.ProgressionProgressPoints",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -58,6 +58,12 @@ Hooks.once("setup", function () {
|
|||
"armorTypesAbbr",
|
||||
"armorMaterialTypes",
|
||||
"armorMaterialTypesAbbr",
|
||||
"armorMaterialTypes",
|
||||
"attributes",
|
||||
"traits",
|
||||
"combatValues",
|
||||
"baseInfo",
|
||||
"progression",
|
||||
];
|
||||
|
||||
// Exclude some from sorting where the default order matters
|
||||
|
|
99
src/module/rolls/roll-executor.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import { DS4RollProvider, RollProvider } from "./roll-provider";
|
||||
|
||||
export function ds4test(testValue: number, rollOptions: RollOptions = new RollOptions()): RollResult {
|
||||
const finalRollValue = testValue;
|
||||
if (finalRollValue <= 20) {
|
||||
return rollCheckSingleDie(finalRollValue, rollOptions);
|
||||
} else {
|
||||
return rollCheckMultipleDice(finalRollValue, rollOptions);
|
||||
}
|
||||
}
|
||||
|
||||
export function rollCheckSingleDie(
|
||||
testValue: number,
|
||||
rollOptions: RollOptions,
|
||||
provider: RollProvider = new DS4RollProvider(),
|
||||
): RollResult {
|
||||
const roll = provider.getNextRoll();
|
||||
const dice = [roll];
|
||||
|
||||
if (roll <= rollOptions.maxCritSucc) {
|
||||
return new RollResult(testValue, RollResultStatus.CRITICAL_SUCCESS, dice);
|
||||
} else if (roll >= rollOptions.minCritFail) {
|
||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice);
|
||||
} else {
|
||||
if (roll <= testValue) {
|
||||
return new RollResult(roll, RollResultStatus.SUCCESS, dice);
|
||||
} else {
|
||||
return new RollResult(0, RollResultStatus.FAILURE, dice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function rollCheckMultipleDice(
|
||||
testValue: number,
|
||||
rollOptions: RollOptions,
|
||||
provider: RollProvider = new DS4RollProvider(),
|
||||
): RollResult {
|
||||
const finalCheck = testValue % 20;
|
||||
const numberOfDice = Math.ceil(testValue / 20);
|
||||
|
||||
const dice = provider.getNextRolls(numberOfDice);
|
||||
|
||||
const firstResult = dice[0];
|
||||
|
||||
if (firstResult >= rollOptions.minCritFail) {
|
||||
return new RollResult(0, RollResultStatus.CRITICAL_FAILURE, dice);
|
||||
}
|
||||
|
||||
const partitionCallback = (prev: [Array<number>, Array<number>], cur: number) => {
|
||||
if (cur <= rollOptions.maxCritSucc) {
|
||||
prev[0].push(cur);
|
||||
} else {
|
||||
prev[1].push(cur);
|
||||
}
|
||||
return prev;
|
||||
};
|
||||
|
||||
const [critSuccesses, otherRolls] = dice
|
||||
.reduce(partitionCallback, [[], []])
|
||||
.map((a) => a.sort((r1, r2) => r2 - r1));
|
||||
|
||||
const sortedRollResults: Array<number> = critSuccesses.concat(otherRolls);
|
||||
|
||||
const evaluationResult = sortedRollResults
|
||||
.map((value, index) => {
|
||||
if (index == numberOfDice - 1) {
|
||||
if (value == 1) {
|
||||
return finalCheck;
|
||||
} else {
|
||||
return value <= finalCheck ? value : 0;
|
||||
}
|
||||
} else {
|
||||
if (value <= rollOptions.maxCritSucc) {
|
||||
return 20;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
})
|
||||
.reduce((a, b) => a + b);
|
||||
|
||||
return new RollResult(evaluationResult, RollResultStatus.SUCCESS, sortedRollResults);
|
||||
}
|
||||
|
||||
export class RollOptions {
|
||||
public maxCritSucc = 1;
|
||||
public minCritFail = 20;
|
||||
}
|
||||
|
||||
export class RollResult {
|
||||
constructor(public value: number, public status: RollResultStatus, public dice: Array<number>) {}
|
||||
}
|
||||
|
||||
export enum RollResultStatus {
|
||||
FAILURE,
|
||||
SUCCESS,
|
||||
CRITICAL_FAILURE,
|
||||
CRITICAL_SUCCESS,
|
||||
}
|
16
src/module/rolls/roll-provider.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
export class DS4RollProvider {
|
||||
getNextRoll(): number {
|
||||
return new Roll("1d20").roll().total;
|
||||
}
|
||||
|
||||
getNextRolls(amount: number): Array<number> {
|
||||
return Array(amount)
|
||||
.fill(0)
|
||||
.map(() => this.getNextRoll());
|
||||
}
|
||||
}
|
||||
|
||||
export interface RollProvider {
|
||||
getNextRoll(): number;
|
||||
getNextRolls(number: number): Array<number>;
|
||||
}
|
99
src/scss/components/_character_values.scss
Normal file
|
@ -0,0 +1,99 @@
|
|||
header.sheet-header {
|
||||
.character-values {
|
||||
flex: 0 0 100%;
|
||||
.attributes-traits {
|
||||
margin-top: $margin-sm;
|
||||
.attribute {
|
||||
.attribute-label {
|
||||
font-family: $font-heading;
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
.attribute-value {
|
||||
border: 2px groove $c-border-groove;
|
||||
line-height: 26px;
|
||||
font-size: 1.5em;
|
||||
text-align: center;
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
gap: 0;
|
||||
input,
|
||||
.attribute-value-total {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
.trait {
|
||||
.trait-label {
|
||||
color: transparent;
|
||||
font-family: $font-heading;
|
||||
font-size: 2em;
|
||||
text-align: center;
|
||||
//text-shadow: -1px 1px 0 $c-black, 1px 1px 0 $c-black, 1px -1px 0 $c-black, -1px -1px 0 $c-black;
|
||||
-webkit-text-stroke: 1px $c-black;
|
||||
}
|
||||
.trait-value {
|
||||
border: 2px groove $c-border-groove;
|
||||
font-size: 1.5em;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
gap: 0;
|
||||
input,
|
||||
.trait-value-total {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.combat-values {
|
||||
margin-top: $margin-sm;
|
||||
.combat-value-with-formula {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
$size: 60px;
|
||||
row-gap: $margin-sm;
|
||||
.combat-value {
|
||||
@include centered-content;
|
||||
height: $size;
|
||||
width: $size;
|
||||
flex: 0 0 auto;
|
||||
background-size: contain;
|
||||
font-size: 1.5em;
|
||||
&.hitPoints {
|
||||
background-image: url("assets/DS4-HP.png");
|
||||
}
|
||||
&.defense {
|
||||
background-image: url("assets/DS4-DEF.png");
|
||||
}
|
||||
&.initiative {
|
||||
background-image: url("assets/DS4-INI.png");
|
||||
}
|
||||
&.movement {
|
||||
background-image: url("assets/DS4-MR.png");
|
||||
}
|
||||
&.meleeAttack {
|
||||
background-image: url("assets/DS4-MAT.png");
|
||||
}
|
||||
&.rangedAttack {
|
||||
background-image: url("assets/DS4-RAT.png");
|
||||
}
|
||||
&.spellcasting {
|
||||
background-image: url("assets/DS4-SPC.png");
|
||||
}
|
||||
&.targetedSpellcasting {
|
||||
background-image: url("assets/DS4-TSC.png");
|
||||
}
|
||||
}
|
||||
|
||||
.combat-value-formula {
|
||||
width: $size;
|
||||
input {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
$header-top-margin: 5px;
|
||||
|
||||
header.sheet-header {
|
||||
flex: 0 0 210px;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
margin-top: 0px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,4 +60,4 @@
|
|||
padding-left: 1em;
|
||||
border-bottom: 2px groove $c-border-groove;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,3 +70,7 @@
|
|||
.flex-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-around {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
|
|
@ -14,3 +14,8 @@
|
|||
@mixin hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@mixin centered-content {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
$padding-sm: 5px;
|
||||
$padding-md: 10px;
|
||||
$padding-lg: 20px;
|
||||
$margin-sm: $padding-sm;
|
||||
$margin-md: $padding-md;
|
||||
$margin-lg: $padding-lg;
|
||||
|
|
|
@ -6,33 +6,95 @@
|
|||
"templates": [],
|
||||
"attributes": {
|
||||
"body": {
|
||||
"initial": 8
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"mobility": {
|
||||
"initial": 0
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"mind": {
|
||||
"initial": 0
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
}
|
||||
},
|
||||
"traits": {
|
||||
"strength": {
|
||||
"initial": 4
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"constitution": {
|
||||
"initial": 0
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"agility": {
|
||||
"initial": 0
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"dexterity": {
|
||||
"initial": 0
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"intellect": {
|
||||
"initial": 0
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"aura": {
|
||||
"initial": 0
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
}
|
||||
},
|
||||
"combatValues": {
|
||||
"hitPoints": {
|
||||
"base": 0,
|
||||
"mod": 0,
|
||||
"current": 0
|
||||
},
|
||||
"defense": {
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"initiative": {
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"movement": {
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"meleeAttack": {
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"rangedAttack": {
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"spellcasting": {
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
},
|
||||
"targetedSpellcasting": {
|
||||
"base": 0,
|
||||
"mod": 0
|
||||
}
|
||||
},
|
||||
"baseInfo": {
|
||||
"race": "",
|
||||
"class": "",
|
||||
"heroClass": "",
|
||||
"racialAbilities": ""
|
||||
},
|
||||
"progression": {
|
||||
"level": 0,
|
||||
"experiencePoints": 0,
|
||||
"talentPoints": {
|
||||
"total": 0,
|
||||
"used": 0
|
||||
},
|
||||
"progressPoints": {
|
||||
"total": 0,
|
||||
"used": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,45 +4,230 @@
|
|||
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100" />
|
||||
<div class="header-fields">
|
||||
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name" /></h1>
|
||||
</div>
|
||||
<div class="character-values">
|
||||
{{!-- The grid classes are defined in scss/global/_grid.scss. To use, use both the "grid" and "grid-Ncol"
|
||||
class where "N" can be any number from 1 to 12 and will create that number of columns. --}}
|
||||
<div class="resources grid grid-2col">
|
||||
<div class="base-infos grid grid-3col">
|
||||
{{!-- "flex-group-center" is also defined in the _grid.scss file and it will add a small amount of
|
||||
padding, a border, and will center all of its child elements content and text. --}}
|
||||
<div class="resource flex-group-center">
|
||||
<label for="data.health.value" class="resource-label">Health</label>
|
||||
<div class="resource-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.health.value" value="{{data.health.value}}" data-dtype="Number" />
|
||||
<span> / </span>
|
||||
<input type="text" name="data.health.max" value="{{data.health.max}}" data-dtype="Number" />
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.baseInfo.race" class="base-info-label">{{config.baseInfo.race}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.baseInfo.race" value="{{data.baseInfo.race}}"
|
||||
data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource flex-group-center">
|
||||
<label for="data.power.value" class="resource-label">Power</label>
|
||||
<div class="resource-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.power.value" value="{{data.power.value}}" data-dtype="Number" />
|
||||
<span> / </span>
|
||||
<input type="text" name="data.power.max" value="{{data.power.max}}" data-dtype="Number" />
|
||||
<div class="base-info flex-group-center">
|
||||
<div class="grid grid-3col">
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.progression.level"
|
||||
class="base-info-label">{{config.progression.level}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.progression.level" value="{{data.progression.level}}"
|
||||
data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.progression.progressPoints"
|
||||
class="base-info-label">{{config.progression.progressPoints}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.progression.progressPoints.used"
|
||||
value="{{data.progression.progressPoints.used}}" data-dtype="Number" /><span> /
|
||||
</span><input type="text" name="data.progression.progressPoints.total"
|
||||
value="{{data.progression.progressPoints.total}}" data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.progression.talentPoints"
|
||||
class="base-info-label">{{config.progression.talentPoints}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.progression.talentPoints.used"
|
||||
value="{{data.progression.talentPoints.used}}" data-dtype="Number" /><span> /
|
||||
</span><input type="text" name="data.progression.talentPoints.total"
|
||||
value="{{data.progression.talentPoints.total}}" data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.baseInfo.class" class="base-info-label">{{config.baseInfo.class}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.baseInfo.class" value="{{data.baseInfo.class}}"
|
||||
data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.baseInfo.racialAbilities"
|
||||
class="base-info-label">{{config.baseInfo.racialAbilities}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.baseInfo.racialAbilities"
|
||||
value="{{data.baseInfo.racialAbilities}}" data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.progression.experiencePoints"
|
||||
class="base-info-label">{{config.progression.experiencePoints}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.progression.experiencePoints"
|
||||
value="{{data.progression.experiencePoints}}" data-dtype="Number" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="base-info flex-group-center">
|
||||
<label for="data.baseInfo.heroClass" class="base-info-label">{{config.baseInfo.heroClass}}</label>
|
||||
<div class="base-info-content flexrow flex-center flex-between">
|
||||
<input type="text" name="data.baseInfo.heroClass" value="{{data.baseInfo.heroClass}}"
|
||||
data-dtype="String" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{!-- The grid classes are defined in scss/global/_grid.scss. To use, use both the "grid" and "grid-Ncol"
|
||||
class where "N" can be any number from 1 to 12 and will create that number of columns. --}}
|
||||
<div class="abilities grid grid-3col">
|
||||
{{#each data.abilities as |ability key|}}
|
||||
<div class="ability flexrow flex-group-center">
|
||||
<label for="data.abilities.{{key}}.value" class="resource-label">{{key}}</label>
|
||||
<input
|
||||
type="text"
|
||||
name="data.abilities.{{key}}.value"
|
||||
value="{{ability.value}}"
|
||||
data-dtype="Number"
|
||||
/>
|
||||
<span class="ability-mod rollable" data-roll="d20+@abilities.{{key}}.mod" data-label="{{key}}"
|
||||
>{{numberFormat ability.mod decimals=0 sign=true}}</span
|
||||
>
|
||||
<div class="attributes-traits grid grid-3col">
|
||||
<div class="attribute flexrow"><label for="data.attributes.body.total"
|
||||
class="attribute-label ">{{config.attributes.body}}</label>
|
||||
<div class="attribute-value flex15 grid grid-8col"><input type="number"
|
||||
name="data.attributes.body.base" value='{{data.attributes.body.base}}'
|
||||
data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.attributes.body.mod" value='{{data.attributes.body.mod}}'
|
||||
data-dtype="Number" /><span> =
|
||||
</span><span class="attribute-value-total">{{data.attributes.body.total}}</span></div>
|
||||
</div>
|
||||
<div class="attribute flexrow"><label for="data.attributes.mobility.total"
|
||||
class="attribute-label">{{config.attributes.mobility}}</label>
|
||||
<div class="attribute-value flex15 grid grid-8col"><input type="number"
|
||||
name="data.attributes.mobility.base" value='{{data.attributes.mobility.base}}'
|
||||
data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.attributes.mobility.mod"
|
||||
value='{{data.attributes.mobility.mod}}' data-dtype="Number" /><span> =
|
||||
</span><span class="attribute-value-total">{{data.attributes.mobility.total}}</span></div>
|
||||
</div>
|
||||
<div class="attribute flexrow"><label for="data.attributes.mind.total"
|
||||
class="attribute-label">{{config.attributes.mind}}</label>
|
||||
<div class="attribute-value flex15 grid grid-8col"><input type="number"
|
||||
name="data.attributes.mind.base" value='{{data.attributes.mind.base}}'
|
||||
data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.attributes.mind.mod" value='{{data.attributes.mind.mod}}'
|
||||
data-dtype="Number" /><span> =
|
||||
</span><span class="attribute-value-total">{{data.attributes.mind.total}}</span></div>
|
||||
</div>
|
||||
<div class="trait flexrow"><label for="data.traits.strength.total"
|
||||
class="trait-label">{{config.traits.strength}}</label>
|
||||
<div class="trait-value flex15 grid grid-8col"><input type="number" name="data.traits.strength.base"
|
||||
value='{{data.traits.strength.base}}' data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.traits.strength.mod" value='{{data.traits.strength.mod}}'
|
||||
data-dtype="Number" /><span> =
|
||||
</span><span class="trait-value-total">{{data.traits.strength.total}}</span></div>
|
||||
</div>
|
||||
<div class="trait flexrow"><label for="data.traits.agility.total"
|
||||
class="trait-label">{{config.traits.agility}}</label>
|
||||
<div class="trait-value flex15 grid grid-8col"><input type="number" name="data.traits.agility.base"
|
||||
value='{{data.traits.agility.base}}' data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.traits.agility.mod" value='{{data.traits.agility.mod}}'
|
||||
data-dtype="Number" /><span> =
|
||||
</span><span class="trait-value-total">{{data.traits.agility.total}}</span></div>
|
||||
</div>
|
||||
<div class="trait flexrow"><label for="data.traits.intellect.total"
|
||||
class="trait-label">{{config.traits.intellect}}</label>
|
||||
<div class="trait-value flex15 grid grid-8col"><input type="number"
|
||||
name="data.traits.intellect.base" value='{{data.traits.intellect.base}}'
|
||||
data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.traits.intellect.mod"
|
||||
value='{{data.traits.intellect.mod}}' data-dtype="Number" /><span> =
|
||||
</span><span class="trait-value-total">{{data.traits.intellect.total}}</span></div>
|
||||
</div>
|
||||
<div class="trait flexrow"><label for="data.traits.constitution.total"
|
||||
class="trait-label">{{config.traits.constitution}}</label>
|
||||
<div class="trait-value flex15 grid grid-8col"><input type="number"
|
||||
name="data.traits.constitution.base" value='{{data.traits.constitution.base}}'
|
||||
data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.traits.constitution.mod"
|
||||
value='{{data.traits.constitution.mod}}' data-dtype="Number" /><span> =
|
||||
</span><span class="trait-value-total">{{data.traits.constitution.total}}</span></div>
|
||||
</div>
|
||||
<div class="trait flexrow"><label for="data.traits.dexterity.total"
|
||||
class="trait-label">{{config.traits.dexterity}}</label>
|
||||
<div class="trait-value flex15 grid grid-8col"><input type="number"
|
||||
name="data.traits.dexterity.base" value='{{data.traits.dexterity.base}}'
|
||||
data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.traits.dexterity.mod"
|
||||
value='{{data.traits.dexterity.mod}}' data-dtype="Number" /><span> =
|
||||
</span><span class="trait-value-total">{{data.traits.dexterity.total}}</span></div>
|
||||
</div>
|
||||
<div class="trait flexrow"><label for="data.traits.aura.total"
|
||||
class="trait-label">{{config.traits.aura}}</label>
|
||||
<div class="trait-value flex15 grid grid-8col"><input type="number" name="data.traits.aura.base"
|
||||
value='{{data.traits.aura.base}}' data-dtype="Number" /><span> +
|
||||
</span><input type="number" name="data.traits.aura.mod" value='{{data.traits.aura.mod}}'
|
||||
data-dtype="Number" /><span> =
|
||||
</span><span class="trait-value-total">{{data.traits.aura.total}}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="combat-values flexrow flex-between">
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value hitPoints"><span
|
||||
class="combat-value-value-total">{{data.combatValues.hitPoints.total}}</span></div>
|
||||
<div class="combat-value-formula flexrow"><input type="number"
|
||||
name="data.combatValues.hitPoints.base" value='{{data.combatValues.hitPoints.base}}'
|
||||
data-dtype="Number" /><input type="number" name="data.combatValues.hitPoints.mod"
|
||||
value='{{data.combatValues.hitPoints.mod}}' data-dtype="Number" /></div>
|
||||
</div>
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value defense"><span
|
||||
class="combat-value-value-total">{{data.combatValues.defense.total}}</span></div>
|
||||
<div class="combat-value-formula flexrow"><input type="number" name="data.combatValues.defense.base"
|
||||
value='{{data.combatValues.defense.base}}' data-dtype="Number" /><input type="number"
|
||||
name="data.combatValues.defense.mod" value='{{data.combatValues.defense.mod}}'
|
||||
data-dtype="Number" /></div>
|
||||
</div>
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value initiative"><span
|
||||
class="combat-value-value-total">{{data.combatValues.initiative.total}}</span></div>
|
||||
<div class="combat-value-formula flexrow"><input type="number"
|
||||
name="data.combatValues.initiative.base" value='{{data.combatValues.initiative.base}}'
|
||||
data-dtype="Number" /><input type="number" name="data.combatValues.initiative.mod"
|
||||
value='{{data.combatValues.initiative.mod}}' data-dtype="Number" /></div>
|
||||
</div>
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value movement"><span
|
||||
class="combat-value-value-total">{{data.combatValues.movement.total}}</span></div>
|
||||
<div class="combat-value-formula flexrow"><input type="number"
|
||||
name="data.combatValues.movement.base" value='{{data.combatValues.movement.base}}'
|
||||
data-dtype="Number" /><input type="number" name="data.combatValues.movement.mod"
|
||||
value='{{data.combatValues.movement.mod}}' data-dtype="Number" /></div>
|
||||
</div>
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value meleeAttack"><span
|
||||
class="combat-value-value-total">{{data.combatValues.meleeAttack.total}}</span></div>
|
||||
<div class="combat-value-formula flexrow"><input type="number"
|
||||
name="data.combatValues.meleeAttack.base" value='{{data.combatValues.meleeAttack.base}}'
|
||||
data-dtype="Number" /><input type="number" name="data.combatValues.meleeAttack.mod"
|
||||
value='{{data.combatValues.meleeAttack.mod}}' data-dtype="Number" /></div>
|
||||
</div>
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value rangedAttack"><span
|
||||
class="combat-value-value-total">{{data.combatValues.rangedAttack.total}}</span></div>
|
||||
<div class="combat-value-formula flexrow"><input type="number"
|
||||
name="data.combatValues.rangedAttack.base" value='{{data.combatValues.rangedAttack.base}}'
|
||||
data-dtype="Number" /><input type="number" name="data.combatValues.rangedAttack.mod"
|
||||
value='{{data.combatValues.rangedAttack.mod}}' data-dtype="Number" /></div>
|
||||
</div>
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value spellcasting"><span
|
||||
class="combat-value-value-total">{{data.combatValues.spellcasting.total}}</span></div>
|
||||
<div class="combat-value-formula flexrow"><input type="number"
|
||||
name="data.combatValues.spellcasting.base" value='{{data.combatValues.spellcasting.base}}'
|
||||
data-dtype="Number" /><input type="number" name="data.combatValues.spellcasting.mod"
|
||||
value='{{data.combatValues.spellcasting.mod}}' data-dtype="Number" /></div>
|
||||
</div>
|
||||
<div class="combat-value-with-formula">
|
||||
<div class="combat-value targetedSpellcasting"><span
|
||||
class="combat-value-value-total">{{data.combatValues.targetedSpellcasting.total}}</span>
|
||||
</div>
|
||||
<div class="combat-value-formula flexrow"><input type="number"
|
||||
name="data.combatValues.targetedSpellcasting.base"
|
||||
value='{{data.combatValues.targetedSpellcasting.base}}' data-dtype="Number" /><input
|
||||
type="number" name="data.combatValues.targetedSpellcasting.mod"
|
||||
value='{{data.combatValues.targetedSpellcasting.mod}}' data-dtype="Number" /></div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -63,4 +248,4 @@
|
|||
{{!-- Items Tab --}}
|
||||
{{> systems/ds4/templates/actor/partials/items.hbs}}
|
||||
</section>
|
||||
</form>
|
||||
</form>
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["DOM", "ES6", "ES2017"],
|
||||
"types": ["foundry-pc-types"]
|
||||
}
|
||||
}
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2017",
|
||||
"lib": ["DOM", "ES6", "ES2017"],
|
||||
"types": ["foundry-pc-types"],
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
|