switch to using TypeScript
This commit is contained in:
parent
1d120b273a
commit
d163fd27fe
53 changed files with 2875 additions and 1614 deletions
|
@ -4,6 +4,6 @@ root = true
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 4
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
|
|
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -5,3 +5,9 @@
|
||||||
# Node Modules
|
# Node Modules
|
||||||
node_modules/
|
node_modules/
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
|
|
||||||
|
# Local configuration
|
||||||
|
foundryconfig.json
|
||||||
|
|
||||||
|
# Distribution files
|
||||||
|
dist
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"trailingComma": "es5",
|
"trailingComma": "es5",
|
||||||
"tabWidth": 2,
|
"tabWidth": 4,
|
||||||
"semi": true,
|
"semi": true,
|
||||||
"singleQuote": false,
|
"singleQuote": false,
|
||||||
"printWidth": 120
|
"printWidth": 120
|
||||||
|
|
436
css/ds4.css
436
css/ds4.css
|
@ -1,436 +0,0 @@
|
||||||
@import url("https://fonts.googleapis.com/css2?family=Lora:wght@400;700&display=swap");
|
|
||||||
@font-face {
|
|
||||||
font-family: "Wood Stamp";
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: normal;
|
|
||||||
src: local("Wood Stamp"), url("../fonts/Woodstamp.woff") format("woff");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Global styles */
|
|
||||||
.window-app {
|
|
||||||
font-family: "Lora", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rollable:hover, .rollable:focus {
|
|
||||||
color: #000;
|
|
||||||
text-shadow: 0 0 10px red;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid,
|
|
||||||
.grid-2col {
|
|
||||||
display: grid;
|
|
||||||
grid-column: span 2 / span 2;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
gap: 10px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-1col {
|
|
||||||
grid-column: span 1 / span 1;
|
|
||||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-3col {
|
|
||||||
grid-column: span 3 / span 3;
|
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-4col {
|
|
||||||
grid-column: span 4 / span 4;
|
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-5col {
|
|
||||||
grid-column: span 5 / span 5;
|
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-6col {
|
|
||||||
grid-column: span 5 / span 5;
|
|
||||||
grid-template-columns: repeat(5, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-7col {
|
|
||||||
grid-column: span 7 / span 7;
|
|
||||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-8col {
|
|
||||||
grid-column: span 8 / span 8;
|
|
||||||
grid-template-columns: repeat(8, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-9col {
|
|
||||||
grid-column: span 9 / span 9;
|
|
||||||
grid-template-columns: repeat(9, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-10col {
|
|
||||||
grid-column: span 10 / span 10;
|
|
||||||
grid-template-columns: repeat(10, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-11col {
|
|
||||||
grid-column: span 11 / span 11;
|
|
||||||
grid-template-columns: repeat(11, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-12col {
|
|
||||||
grid-column: span 12 / span 12;
|
|
||||||
grid-template-columns: repeat(12, minmax(0, 1fr));
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-center,
|
|
||||||
.flex-group-left,
|
|
||||||
.flex-group-right {
|
|
||||||
-webkit-box-pack: center;
|
|
||||||
-ms-flex-pack: center;
|
|
||||||
justify-content: center;
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
padding: 5px;
|
|
||||||
border: 1px solid #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-left {
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-group-right {
|
|
||||||
-webkit-box-pack: end;
|
|
||||||
-ms-flex-pack: end;
|
|
||||||
justify-content: flex-end;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
/* Flexbox */
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
.flexrow {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: horizontal;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
-ms-flex-wrap: wrap;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexrow > * {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexrow .flex1 {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexrow .flex2 {
|
|
||||||
-webkit-box-flex: 2;
|
|
||||||
-ms-flex: 2;
|
|
||||||
flex: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexrow .flex3 {
|
|
||||||
-webkit-box-flex: 3;
|
|
||||||
-ms-flex: 3;
|
|
||||||
flex: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexrow .flex4 {
|
|
||||||
-webkit-box-flex: 4;
|
|
||||||
-ms-flex: 4;
|
|
||||||
flex: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexcol {
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: column;
|
|
||||||
flex-direction: column;
|
|
||||||
-ms-flex-wrap: nowrap;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexcol > * {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexcol .flex1 {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexcol .flex2 {
|
|
||||||
-webkit-box-flex: 2;
|
|
||||||
-ms-flex: 2;
|
|
||||||
flex: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexcol .flex3 {
|
|
||||||
-webkit-box-flex: 3;
|
|
||||||
-ms-flex: 3;
|
|
||||||
flex: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flexcol .flex4 {
|
|
||||||
-webkit-box-flex: 4;
|
|
||||||
-ms-flex: 4;
|
|
||||||
flex: 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-center {
|
|
||||||
-webkit-box-align: center;
|
|
||||||
-ms-flex-align: center;
|
|
||||||
align-items: center;
|
|
||||||
-webkit-box-pack: center;
|
|
||||||
-ms-flex-pack: center;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.flex-between {
|
|
||||||
-webkit-box-pack: justify;
|
|
||||||
-ms-flex-pack: justify;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Styles limited to ds4 sheets */
|
|
||||||
.ds4 .window-content {
|
|
||||||
overflow-y: hidden;
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .window-content form {
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .window-content .tab {
|
|
||||||
height: 100%;
|
|
||||||
overflow-y: auto;
|
|
||||||
-ms-flex-line-pack: start;
|
|
||||||
align-content: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .item-form {
|
|
||||||
font-family: "Lora", sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 header.sheet-header {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 210px;
|
|
||||||
flex: 0 0 210px;
|
|
||||||
overflow: hidden;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: horizontal;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
-ms-flex-wrap: wrap;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
-webkit-box-pack: start;
|
|
||||||
-ms-flex-pack: start;
|
|
||||||
justify-content: flex-start;
|
|
||||||
-webkit-box-align: start;
|
|
||||||
-ms-flex-align: start;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 header.sheet-header .profile-img {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 100px;
|
|
||||||
flex: 0 0 100px;
|
|
||||||
height: 100px;
|
|
||||||
margin: 5px 10px 5px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 header.sheet-header .header-fields {
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 header.sheet-header h1.charname {
|
|
||||||
height: 50px;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 5px 10px 5px 0;
|
|
||||||
border-bottom: 0;
|
|
||||||
font-family: "Wood Stamp", sans-serif;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 header.sheet-header h1.charname input {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
border: none;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 header.sheet-header h2.item-type {
|
|
||||||
font-family: "Wood Stamp", sans-serif;
|
|
||||||
display: block;
|
|
||||||
height: 50px;
|
|
||||||
padding: 0px;
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 0px;
|
|
||||||
flex: 0 0 0;
|
|
||||||
color: #777;
|
|
||||||
border: none;
|
|
||||||
line-height: 50px;
|
|
||||||
margin: 5px 0;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .sheet-tabs {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0;
|
|
||||||
flex: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .sheet-body,
|
|
||||||
.ds4 .sheet-body .tab {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .basic-properties {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 100%;
|
|
||||||
flex: 0 0 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .basic-properties .basic-property .basic-property-label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .basic-properties .basic-property .basic-property-select {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 nav.tabs {
|
|
||||||
height: 40px;
|
|
||||||
border-top: 2px groove #eeede0;
|
|
||||||
border-bottom: 2px groove #eeede0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 nav.tabs .item {
|
|
||||||
line-height: 40px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 nav.tabs .item.active {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .items-list {
|
|
||||||
list-style: none;
|
|
||||||
margin: 7px 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .items-list .item-header {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .items-list .item {
|
|
||||||
height: 30px;
|
|
||||||
line-height: 24px;
|
|
||||||
padding: 3px 0;
|
|
||||||
border-bottom: 1px solid #BBB;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .items-list .item .item-image {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 24px;
|
|
||||||
flex: 0 0 24px;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .items-list .item img {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .items-list .item-name {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .items-list .item-controls {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 86px;
|
|
||||||
flex: 0 0 86px;
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .side-properties {
|
|
||||||
-webkit-box-flex: 0;
|
|
||||||
-ms-flex: 0 0 150px;
|
|
||||||
flex: 0 0 150px;
|
|
||||||
margin: 5px 5px 5px 0;
|
|
||||||
padding-right: 5px;
|
|
||||||
border-right: 2px groove #eeede0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .side-properties .side-property {
|
|
||||||
margin: 2px 0;
|
|
||||||
display: -webkit-box;
|
|
||||||
display: -ms-flexbox;
|
|
||||||
display: flex;
|
|
||||||
-webkit-box-orient: horizontal;
|
|
||||||
-webkit-box-direction: normal;
|
|
||||||
-ms-flex-direction: row;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .side-properties .side-property label {
|
|
||||||
-webkit-box-flex: 2;
|
|
||||||
-ms-flex: 2;
|
|
||||||
flex: 2;
|
|
||||||
line-height: 26px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .side-properties .side-property input,
|
|
||||||
.ds4 .side-properties .side-property select {
|
|
||||||
text-align: right;
|
|
||||||
-webkit-box-flex: 1;
|
|
||||||
-ms-flex: 1;
|
|
||||||
flex: 1;
|
|
||||||
width: calc(100% - 2px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .sheet-body .tab .editor {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .tox .tox-editor-container {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ds4 .tox .tox-edit-area {
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
534
gulpfile.js
534
gulpfile.js
|
@ -1,50 +1,504 @@
|
||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const prefix = require('gulp-autoprefixer');
|
const fs = require('fs-extra');
|
||||||
const sourcemaps = require('gulp-sourcemaps');
|
const path = require('path');
|
||||||
|
const chalk = require('chalk');
|
||||||
|
const archiver = require('archiver');
|
||||||
|
const stringify = require('json-stringify-pretty-compact');
|
||||||
|
const typescript = require('typescript');
|
||||||
|
|
||||||
|
const ts = require('gulp-typescript');
|
||||||
|
const less = require('gulp-less');
|
||||||
const sass = require('gulp-sass');
|
const sass = require('gulp-sass');
|
||||||
|
const git = require('gulp-git');
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
const argv = require('yargs').argv;
|
||||||
/* Compile Sass
|
|
||||||
/* ----------------------------------------- */
|
|
||||||
|
|
||||||
// Small error handler helper function.
|
sass.compiler = require('sass');
|
||||||
function handleError(err) {
|
|
||||||
console.log(err.toString());
|
function getConfig() {
|
||||||
this.emit('end');
|
const configPath = path.resolve(process.cwd(), 'foundryconfig.json');
|
||||||
|
let config;
|
||||||
|
|
||||||
|
if (fs.existsSync(configPath)) {
|
||||||
|
config = fs.readJSONSync(configPath);
|
||||||
|
return config;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SYSTEM_SCSS = ["scss/**/*.scss"];
|
function getManifest() {
|
||||||
function compileScss() {
|
const json = {};
|
||||||
// Configure options for sass output. For example, 'expanded' or 'nested'
|
|
||||||
let options = {
|
if (fs.existsSync('src')) {
|
||||||
outputStyle: 'expanded'
|
json.root = 'src';
|
||||||
};
|
} else {
|
||||||
return gulp.src(SYSTEM_SCSS)
|
json.root = 'dist';
|
||||||
.pipe(
|
}
|
||||||
sass(options)
|
|
||||||
.on('error', handleError)
|
const modulePath = path.join(json.root, 'module.json');
|
||||||
|
const systemPath = path.join(json.root, 'system.json');
|
||||||
|
|
||||||
|
if (fs.existsSync(modulePath)) {
|
||||||
|
json.file = fs.readJSONSync(modulePath);
|
||||||
|
json.name = 'module.json';
|
||||||
|
} else if (fs.existsSync(systemPath)) {
|
||||||
|
json.file = fs.readJSONSync(systemPath);
|
||||||
|
json.name = 'system.json';
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TypeScript transformers
|
||||||
|
* @returns {typescript.TransformerFactory<typescript.SourceFile>}
|
||||||
|
*/
|
||||||
|
function createTransformer() {
|
||||||
|
/**
|
||||||
|
* @param {typescript.Node} node
|
||||||
|
*/
|
||||||
|
function shouldMutateModuleSpecifier(node) {
|
||||||
|
if (
|
||||||
|
!typescript.isImportDeclaration(node) &&
|
||||||
|
!typescript.isExportDeclaration(node)
|
||||||
)
|
)
|
||||||
.pipe(prefix({
|
return false;
|
||||||
cascade: false
|
if (node.moduleSpecifier === undefined) return false;
|
||||||
}))
|
if (!typescript.isStringLiteral(node.moduleSpecifier)) return false;
|
||||||
.pipe(gulp.dest("./css"))
|
if (
|
||||||
}
|
!node.moduleSpecifier.text.startsWith('./') &&
|
||||||
const css = gulp.series(compileScss);
|
!node.moduleSpecifier.text.startsWith('../')
|
||||||
|
)
|
||||||
/* ----------------------------------------- */
|
return false;
|
||||||
/* Watch Updates
|
if (path.extname(node.moduleSpecifier.text) !== '') return false;
|
||||||
/* ----------------------------------------- */
|
return true;
|
||||||
|
|
||||||
function watchUpdates() {
|
|
||||||
gulp.watch(SYSTEM_SCSS, css);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------- */
|
/**
|
||||||
/* Export Tasks
|
* Transforms import/export declarations to append `.js` extension
|
||||||
/* ----------------------------------------- */
|
* @param {typescript.TransformationContext} context
|
||||||
|
*/
|
||||||
exports.default = gulp.series(
|
function importTransformer(context) {
|
||||||
compileScss,
|
return (node) => {
|
||||||
watchUpdates
|
/**
|
||||||
|
* @param {typescript.Node} node
|
||||||
|
*/
|
||||||
|
function visitor(node) {
|
||||||
|
if (shouldMutateModuleSpecifier(node)) {
|
||||||
|
if (typescript.isImportDeclaration(node)) {
|
||||||
|
const newModuleSpecifier = typescript.createLiteral(
|
||||||
|
`${node.moduleSpecifier.text}.js`
|
||||||
|
);
|
||||||
|
return typescript.updateImportDeclaration(
|
||||||
|
node,
|
||||||
|
node.decorators,
|
||||||
|
node.modifiers,
|
||||||
|
node.importClause,
|
||||||
|
newModuleSpecifier
|
||||||
|
);
|
||||||
|
} else if (typescript.isExportDeclaration(node)) {
|
||||||
|
const newModuleSpecifier = typescript.createLiteral(
|
||||||
|
`${node.moduleSpecifier.text}.js`
|
||||||
|
);
|
||||||
|
return typescript.updateExportDeclaration(
|
||||||
|
node,
|
||||||
|
node.decorators,
|
||||||
|
node.modifiers,
|
||||||
|
node.exportClause,
|
||||||
|
newModuleSpecifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return typescript.visitEachChild(node, visitor, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
return typescript.visitNode(node, visitor);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return importTransformer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tsConfig = ts.createProject('tsconfig.json', {
|
||||||
|
getCustomTransformers: (_program) => ({
|
||||||
|
after: [createTransformer()],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
/********************/
|
||||||
|
/* BUILD */
|
||||||
|
/********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build TypeScript
|
||||||
|
*/
|
||||||
|
function buildTS() {
|
||||||
|
return gulp.src('src/**/*.ts').pipe(tsConfig()).pipe(gulp.dest('dist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Less
|
||||||
|
*/
|
||||||
|
function buildLess() {
|
||||||
|
return gulp.src('src/*.less').pipe(less()).pipe(gulp.dest('dist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build SASS
|
||||||
|
*/
|
||||||
|
function buildSASS() {
|
||||||
|
return gulp
|
||||||
|
.src('src/*.scss')
|
||||||
|
.pipe(sass().on('error', sass.logError))
|
||||||
|
.pipe(gulp.dest('dist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy static files
|
||||||
|
*/
|
||||||
|
async function copyFiles() {
|
||||||
|
const statics = [
|
||||||
|
'lang',
|
||||||
|
'fonts',
|
||||||
|
'assets',
|
||||||
|
'templates',
|
||||||
|
'module.json',
|
||||||
|
'system.json',
|
||||||
|
'template.json',
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
for (const file of statics) {
|
||||||
|
if (fs.existsSync(path.join('src', file))) {
|
||||||
|
await fs.copy(path.join('src', file), path.join('dist', file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
} catch (err) {
|
||||||
|
Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watch for changes for each build step
|
||||||
|
*/
|
||||||
|
function buildWatch() {
|
||||||
|
gulp.watch('src/**/*.ts', { ignoreInitial: false }, buildTS);
|
||||||
|
gulp.watch('src/**/*.less', { ignoreInitial: false }, buildLess);
|
||||||
|
gulp.watch('src/**/*.scss', { ignoreInitial: false }, buildSASS);
|
||||||
|
gulp.watch(
|
||||||
|
['src/fonts', 'src/lang', 'src/templates', 'src/*.json'],
|
||||||
|
{ ignoreInitial: false },
|
||||||
|
copyFiles
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************/
|
||||||
|
/* CLEAN */
|
||||||
|
/********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove built files from `dist` folder
|
||||||
|
* while ignoring source files
|
||||||
|
*/
|
||||||
|
async function clean() {
|
||||||
|
const name = path.basename(path.resolve('.'));
|
||||||
|
const files = [];
|
||||||
|
|
||||||
|
// If the project uses TypeScript
|
||||||
|
if (fs.existsSync(path.join('src', `${name}.ts`))) {
|
||||||
|
files.push(
|
||||||
|
'lang',
|
||||||
|
'templates',
|
||||||
|
'assets',
|
||||||
|
'module',
|
||||||
|
`${name}.js`,
|
||||||
|
'module.json',
|
||||||
|
'system.json',
|
||||||
|
'template.json'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the project uses Less or SASS
|
||||||
|
if (
|
||||||
|
fs.existsSync(path.join('src', `${name}.less`)) ||
|
||||||
|
fs.existsSync(path.join('src', `${name}.scss`))
|
||||||
|
) {
|
||||||
|
files.push('fonts', `${name}.css`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(' ', chalk.yellow('Files to clean:'));
|
||||||
|
console.log(' ', chalk.blueBright(files.join('\n ')));
|
||||||
|
|
||||||
|
// Attempt to remove the files
|
||||||
|
try {
|
||||||
|
for (const filePath of files) {
|
||||||
|
await fs.remove(path.join('dist', filePath));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
} catch (err) {
|
||||||
|
Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************/
|
||||||
|
/* LINK */
|
||||||
|
/********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link build to User Data folder
|
||||||
|
*/
|
||||||
|
async function linkUserData() {
|
||||||
|
const name = path.basename(path.resolve('.'));
|
||||||
|
const config = fs.readJSONSync('foundryconfig.json');
|
||||||
|
|
||||||
|
let destDir;
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
fs.existsSync(path.resolve('.', 'dist', 'module.json')) ||
|
||||||
|
fs.existsSync(path.resolve('.', 'src', 'module.json'))
|
||||||
|
) {
|
||||||
|
destDir = 'modules';
|
||||||
|
} else if (
|
||||||
|
fs.existsSync(path.resolve('.', 'dist', 'system.json')) ||
|
||||||
|
fs.existsSync(path.resolve('.', 'src', 'system.json'))
|
||||||
|
) {
|
||||||
|
destDir = 'systems';
|
||||||
|
} else {
|
||||||
|
throw Error(
|
||||||
|
`Could not find ${chalk.blueBright(
|
||||||
|
'module.json'
|
||||||
|
)} or ${chalk.blueBright('system.json')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let linkDir;
|
||||||
|
if (config.dataPath) {
|
||||||
|
if (!fs.existsSync(path.join(config.dataPath, 'Data')))
|
||||||
|
throw Error('User Data path invalid, no Data directory found');
|
||||||
|
|
||||||
|
linkDir = path.join(config.dataPath, 'Data', destDir, name);
|
||||||
|
} else {
|
||||||
|
throw Error('No User Data path defined in foundryconfig.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.clean || argv.c) {
|
||||||
|
console.log(
|
||||||
|
chalk.yellow(`Removing build in ${chalk.blueBright(linkDir)}`)
|
||||||
|
);
|
||||||
|
|
||||||
|
await fs.remove(linkDir);
|
||||||
|
} else if (!fs.existsSync(linkDir)) {
|
||||||
|
console.log(
|
||||||
|
chalk.green(`Copying build to ${chalk.blueBright(linkDir)}`)
|
||||||
|
);
|
||||||
|
await fs.symlink(path.resolve('./dist'), linkDir);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
} catch (err) {
|
||||||
|
Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************/
|
||||||
|
/* PACKAGE */
|
||||||
|
/*********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Package build
|
||||||
|
*/
|
||||||
|
async function packageBuild() {
|
||||||
|
const manifest = getManifest();
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
// Remove the package dir without doing anything else
|
||||||
|
if (argv.clean || argv.c) {
|
||||||
|
console.log(chalk.yellow('Removing all packaged files'));
|
||||||
|
fs.removeSync('package');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure there is a directory to hold all the packaged versions
|
||||||
|
fs.ensureDirSync('package');
|
||||||
|
|
||||||
|
// Initialize the zip file
|
||||||
|
const zipName = `${manifest.file.name}-v${manifest.file.version}.zip`;
|
||||||
|
const zipFile = fs.createWriteStream(path.join('package', zipName));
|
||||||
|
const zip = archiver('zip', { zlib: { level: 9 } });
|
||||||
|
|
||||||
|
zipFile.on('close', () => {
|
||||||
|
console.log(chalk.green(zip.pointer() + ' total bytes'));
|
||||||
|
console.log(
|
||||||
|
chalk.green(`Zip file ${zipName} has been written`)
|
||||||
|
);
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
zip.on('error', (err) => {
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
zip.pipe(zipFile);
|
||||||
|
|
||||||
|
// Add the directory with the final code
|
||||||
|
zip.directory('dist/', manifest.file.name);
|
||||||
|
|
||||||
|
zip.finalize();
|
||||||
|
} catch (err) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************/
|
||||||
|
/* PACKAGE */
|
||||||
|
/*********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update version and URLs in the manifest JSON
|
||||||
|
*/
|
||||||
|
function updateManifest(cb) {
|
||||||
|
const packageJson = fs.readJSONSync('package.json');
|
||||||
|
const config = getConfig(),
|
||||||
|
manifest = getManifest(),
|
||||||
|
rawURL = config.rawURL,
|
||||||
|
repoURL = config.repository,
|
||||||
|
manifestRoot = manifest.root;
|
||||||
|
|
||||||
|
if (!config) cb(Error(chalk.red('foundryconfig.json not found')));
|
||||||
|
if (!manifest) cb(Error(chalk.red('Manifest JSON not found')));
|
||||||
|
if (!rawURL || !repoURL)
|
||||||
|
cb(
|
||||||
|
Error(
|
||||||
|
chalk.red(
|
||||||
|
'Repository URLs not configured in foundryconfig.json'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const version = argv.update || argv.u;
|
||||||
|
|
||||||
|
/* Update version */
|
||||||
|
|
||||||
|
const versionMatch = /^(\d{1,}).(\d{1,}).(\d{1,})$/;
|
||||||
|
const currentVersion = manifest.file.version;
|
||||||
|
let targetVersion = '';
|
||||||
|
|
||||||
|
if (!version) {
|
||||||
|
cb(Error('Missing version number'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionMatch.test(version)) {
|
||||||
|
targetVersion = version;
|
||||||
|
} else {
|
||||||
|
targetVersion = currentVersion.replace(
|
||||||
|
versionMatch,
|
||||||
|
(substring, major, minor, patch) => {
|
||||||
|
console.log(
|
||||||
|
substring,
|
||||||
|
Number(major) + 1,
|
||||||
|
Number(minor) + 1,
|
||||||
|
Number(patch) + 1
|
||||||
|
);
|
||||||
|
if (version === 'major') {
|
||||||
|
return `${Number(major) + 1}.0.0`;
|
||||||
|
} else if (version === 'minor') {
|
||||||
|
return `${major}.${Number(minor) + 1}.0`;
|
||||||
|
} else if (version === 'patch') {
|
||||||
|
return `${major}.${minor}.${Number(patch) + 1}`;
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetVersion === '') {
|
||||||
|
return cb(Error(chalk.red('Error: Incorrect version arguments.')));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetVersion === currentVersion) {
|
||||||
|
return cb(
|
||||||
|
Error(
|
||||||
|
chalk.red(
|
||||||
|
'Error: Target version is identical to current version.'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log(`Updating version number to '${targetVersion}'`);
|
||||||
|
|
||||||
|
packageJson.version = targetVersion;
|
||||||
|
manifest.file.version = targetVersion;
|
||||||
|
|
||||||
|
/* Update URLs */
|
||||||
|
|
||||||
|
const result = `${rawURL}/v${manifest.file.version}/package/${manifest.file.name}-v${manifest.file.version}.zip`;
|
||||||
|
|
||||||
|
manifest.file.url = repoURL;
|
||||||
|
manifest.file.manifest = `${rawURL}/master/${manifestRoot}/${manifest.name}`;
|
||||||
|
manifest.file.download = result;
|
||||||
|
|
||||||
|
const prettyProjectJson = stringify(manifest.file, {
|
||||||
|
maxLength: 35,
|
||||||
|
indent: '\t',
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.writeJSONSync('package.json', packageJson, { spaces: '\t' });
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(manifest.root, manifest.name),
|
||||||
|
prettyProjectJson,
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
|
||||||
|
return cb();
|
||||||
|
} catch (err) {
|
||||||
|
cb(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function gitAdd() {
|
||||||
|
return gulp.src('package').pipe(git.add({ args: '--no-all' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function gitCommit() {
|
||||||
|
return gulp.src('./*').pipe(
|
||||||
|
git.commit(`v${getManifest().file.version}`, {
|
||||||
|
args: '-a',
|
||||||
|
disableAppendPaths: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function gitTag() {
|
||||||
|
const manifest = getManifest();
|
||||||
|
return git.tag(
|
||||||
|
`v${manifest.file.version}`,
|
||||||
|
`Updated to ${manifest.file.version}`,
|
||||||
|
(err) => {
|
||||||
|
if (err) throw err;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const execGit = gulp.series(gitAdd, gitCommit, gitTag);
|
||||||
|
|
||||||
|
const execBuild = gulp.parallel(buildTS, buildLess, buildSASS, copyFiles);
|
||||||
|
|
||||||
|
exports.build = gulp.series(clean, execBuild);
|
||||||
|
exports.watch = buildWatch;
|
||||||
|
exports.clean = clean;
|
||||||
|
exports.link = linkUserData;
|
||||||
|
exports.package = packageBuild;
|
||||||
|
exports.update = updateManifest;
|
||||||
|
exports.publish = gulp.series(
|
||||||
|
clean,
|
||||||
|
updateManifest,
|
||||||
|
execBuild,
|
||||||
|
packageBuild,
|
||||||
|
execGit
|
||||||
);
|
);
|
||||||
exports.css = css;
|
|
||||||
|
|
39
lang/en.json
39
lang/en.json
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
"DS4.Description": "Description",
|
|
||||||
"DS4.Details": "Details",
|
|
||||||
"DS4.AttackType": "Attack Type",
|
|
||||||
"DS4.AttackTypeAbbr": "AT",
|
|
||||||
"DS4.WeaponBonus": "Weapon Bonus",
|
|
||||||
"DS4.WeaponBonusAbbr": "WB",
|
|
||||||
"DS4.OpponentDefense": "Opponent Defense",
|
|
||||||
"DS4.OpponentDefenseAbbr": "OD",
|
|
||||||
"DS4.AttackTypeMelee": "Melee",
|
|
||||||
"DS4.AttackTypeRanged": "Ranged",
|
|
||||||
"DS4.AttackTypeMeleeRanged": "Melee / Ranged",
|
|
||||||
"DS4.Quantity": "Quantity",
|
|
||||||
"DS4.PriceGold": "Price (Gold)",
|
|
||||||
"DS4.ItemAvailability": "Availability",
|
|
||||||
"DS4.ItemAvailabilityHamlet": "Hamlet",
|
|
||||||
"DS4.ItemAvailabilityVilage": "Village",
|
|
||||||
"DS4.ItemAvailabilityCity": "City",
|
|
||||||
"DS4.ItemAvailabilityElves": "Elves",
|
|
||||||
"DS4.ItemAvailabilityDwarves": "Dwarves",
|
|
||||||
"DS4.ItemAvailabilityNone": "None",
|
|
||||||
"DS4.ItemTypeWeapon": "Weapon",
|
|
||||||
"DS4.ItemTypeArmor": "Armor",
|
|
||||||
"DS4.ItemTypeShield": "Shield",
|
|
||||||
"DS4.ItemTypeTrinket": "Trinket",
|
|
||||||
"DS4.ItemTypeEquipment": "Equipment",
|
|
||||||
"DS4.ArmorType": "Armor Type",
|
|
||||||
"DS4.ArmorMaterialType": "Material Type",
|
|
||||||
"DS4.ArmorValue": "Armor Value",
|
|
||||||
"DS4.ArmorTypeBody": "Body",
|
|
||||||
"DS4.ArmorTypeHelmet": "Helmet",
|
|
||||||
"DS4.ArmorTypeVambrace": "Vambrace",
|
|
||||||
"DS4.ArmorTypeGreaves": "Greaves",
|
|
||||||
"DS4.ArmorTypeVambraceGreaves": "Vambrace + Greaves",
|
|
||||||
"DS4.ArmorMaterialTypeCloth": "Cloth",
|
|
||||||
"DS4.ArmorMaterialTypeLeather": "Leather",
|
|
||||||
"DS4.ArmorMaterialTypeChain": "Chain",
|
|
||||||
"DS4.ArmorMaterialTypePlate": "Plate"
|
|
||||||
}
|
|
0
lib/some-lib/some-lib.min.js
vendored
0
lib/some-lib/some-lib.min.js
vendored
|
@ -1,108 +0,0 @@
|
||||||
/**
|
|
||||||
* Extend the basic ActorSheet with some very simple modifications
|
|
||||||
* @extends {ActorSheet}
|
|
||||||
*/
|
|
||||||
export class DS4ActorSheet extends ActorSheet {
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
return mergeObject(super.defaultOptions, {
|
|
||||||
classes: ["ds4", "sheet", "actor"],
|
|
||||||
template: "systems/ds4/templates/actor/actor-sheet.html",
|
|
||||||
width: 600,
|
|
||||||
height: 600,
|
|
||||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
getData() {
|
|
||||||
const data = super.getData();
|
|
||||||
data.dtypes = ["String", "Number", "Boolean"];
|
|
||||||
for (let attr of Object.values(data.data.attributes)) {
|
|
||||||
attr.isCheckbox = attr.dtype === "Boolean";
|
|
||||||
}
|
|
||||||
console.log(data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
|
|
||||||
// Everything below here is only needed if the sheet is editable
|
|
||||||
if (!this.options.editable) return;
|
|
||||||
|
|
||||||
// Add Inventory Item
|
|
||||||
html.find('.item-create').click(this._onItemCreate.bind(this));
|
|
||||||
|
|
||||||
// Update Inventory Item
|
|
||||||
html.find('.item-edit').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item");
|
|
||||||
const item = this.actor.getOwnedItem(li.data("itemId"));
|
|
||||||
item.sheet.render(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Delete Inventory Item
|
|
||||||
html.find('.item-delete').click(ev => {
|
|
||||||
const li = $(ev.currentTarget).parents(".item");
|
|
||||||
this.actor.deleteOwnedItem(li.data("itemId"));
|
|
||||||
li.slideUp(200, () => this.render(false));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Rollable abilities.
|
|
||||||
html.find('.rollable').click(this._onRoll.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset
|
|
||||||
* @param {Event} event The originating click event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onItemCreate(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const header = event.currentTarget;
|
|
||||||
// Get the type of item to create.
|
|
||||||
const type = header.dataset.type;
|
|
||||||
// Grab any data associated with this control.
|
|
||||||
const data = duplicate(header.dataset);
|
|
||||||
// Initialize a default name.
|
|
||||||
const name = `New ${type.capitalize()}`;
|
|
||||||
// Prepare the item object.
|
|
||||||
const itemData = {
|
|
||||||
name: name,
|
|
||||||
type: type,
|
|
||||||
data: data
|
|
||||||
};
|
|
||||||
// Remove the type from the dataset since it's in the itemData.type prop.
|
|
||||||
delete itemData.data["type"];
|
|
||||||
|
|
||||||
// Finally, create the item!
|
|
||||||
return this.actor.createOwnedItem(itemData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle clickable rolls.
|
|
||||||
* @param {Event} event The originating click event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
_onRoll(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const element = event.currentTarget;
|
|
||||||
const dataset = element.dataset;
|
|
||||||
|
|
||||||
if (dataset.roll) {
|
|
||||||
let roll = new Roll(dataset.roll, this.actor.data.data);
|
|
||||||
let label = dataset.label ? `Rolling ${dataset.label}` : '';
|
|
||||||
roll.roll().toMessage({
|
|
||||||
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
|
||||||
flavor: label
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
/**
|
|
||||||
* 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 {
|
|
||||||
/** @override */
|
|
||||||
prepareDerivedData() {
|
|
||||||
const data = this.data;
|
|
||||||
this._prepareCombatValues(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
_prepareCombatValues(data) {
|
|
||||||
const hitPointsModifier = getProperty(data, "data.combatValues.hitPoints.modifier") || 0;
|
|
||||||
setProperty(
|
|
||||||
data,
|
|
||||||
"data.combatValues.hitPoints.max",
|
|
||||||
data.data.attributes.body.initial + data.data.traits.constitution.initial + 10 + hitPointsModifier
|
|
||||||
);
|
|
||||||
|
|
||||||
const defenseModifier = getProperty(data, "data.combatValues.defense.modifier") || 0;
|
|
||||||
setProperty(
|
|
||||||
data,
|
|
||||||
"data.combatValues.defense.value",
|
|
||||||
data.data.attributes.body.initial +
|
|
||||||
data.data.traits.constitution.initial +
|
|
||||||
this._getArmorValue() +
|
|
||||||
defenseModifier
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_getArmorValue() {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
export const DS4 = {};
|
|
||||||
|
|
||||||
// ASCII Artwork
|
|
||||||
DS4.ASCII = `_____________________________________________________________________________________________
|
|
||||||
____ _ _ _ _ ____ _____ ___ _ _ ____ _ _ __ _______ ____ ____ _ _
|
|
||||||
| _ \\| | | | \\ | |/ ___| ____/ _ \\| \\ | / ___|| | / \\\\ \\ / / ____| _ \\/ ___| | || |
|
|
||||||
| | | | | | | \\| | | _| _|| | | | \\| \\___ \\| | / _ \\\\ V /| _| | |_) \\___ \\ | || |_
|
|
||||||
| |_| | |_| | |\\ | |_| | |__| |_| | |\\ |___) | |___ / ___ \\| | | |___| _ < ___) | |__ _|
|
|
||||||
|____/ \\___/|_| \\_|\\____|_____\\___/|_| \\_|____/|_____/_/ \\_\\_| |_____|_| \\_\\____/ |_|
|
|
||||||
=============================================================================================`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the set of acttack types that can be performed with weapon items
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
DS4.attackTypes = {
|
|
||||||
melee: "DS4.AttackTypeMelee",
|
|
||||||
ranged: "DS4.AttackTypeRanged",
|
|
||||||
meleeRanged: "DS4.AttackTypeMeleeRanged",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the set of item availabilties
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
DS4.itemAvailabilities = {
|
|
||||||
hamlet: "DS4.ItemAvailabilityHamlet",
|
|
||||||
village: "DS4.ItemAvailabilityVilage",
|
|
||||||
city: "DS4.ItemAvailabilityCity",
|
|
||||||
elves: "DS4.ItemAvailabilityElves",
|
|
||||||
dwarves: "DS4.ItemAvailabilityDwarves",
|
|
||||||
none: "DS4.ItemAvailabilityNone",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * Define the set of item types
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
DS4.itemTypes = {
|
|
||||||
weapon: "DS4.ItemTypeWeapon",
|
|
||||||
armor: "DS4.ItemTypeArmor",
|
|
||||||
shield: "DS4.ItemTypeShield",
|
|
||||||
trinket: "DS4.ItemTypeTrinket",
|
|
||||||
equipment: "DS4.ItemTypeEquipment",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * Define the set of armor types, a character may only wear one item of each at any given time
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
DS4.armorTypes = {
|
|
||||||
body: "DS4.ArmorTypeBody",
|
|
||||||
helment: "DS4.ArmorTypeHelmet",
|
|
||||||
vambrace: "DS4.ArmorTypeVambrace",
|
|
||||||
greaves: "DS4.ArmorTypeGreaves",
|
|
||||||
vambraceGreaves: "DS4.ArmorTypeVambraceGreaves",
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
|
|
||||||
* @type {Object}
|
|
||||||
*/
|
|
||||||
DS4.armorMaterialTypes = {
|
|
||||||
cloth: "DS4.ArmorMaterialTypeCloth",
|
|
||||||
leather: "DS4.ArmorMaterialTypeLeather",
|
|
||||||
chain: "DS4.ArmorMaterialTypeChain",
|
|
||||||
plate: "DS4.ArmorMaterialTypePlate",
|
|
||||||
};
|
|
|
@ -1,63 +0,0 @@
|
||||||
// Import Modules
|
|
||||||
import { DS4Actor } from "./actor/actor.js";
|
|
||||||
import { DS4ActorSheet } from "./actor/actor-sheet.js";
|
|
||||||
import { DS4Item } from "./item/item.js";
|
|
||||||
import { DS4ItemSheet } from "./item/item-sheet.js";
|
|
||||||
import { DS4 } from "./config.js";
|
|
||||||
|
|
||||||
Hooks.once("init", async function () {
|
|
||||||
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
|
||||||
|
|
||||||
game.ds4 = {
|
|
||||||
DS4Actor,
|
|
||||||
DS4Item,
|
|
||||||
DS4,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Record configuration
|
|
||||||
CONFIG.DS4 = DS4;
|
|
||||||
|
|
||||||
// Define custom Entity classes
|
|
||||||
CONFIG.Actor.entityClass = DS4Actor;
|
|
||||||
CONFIG.Item.entityClass = DS4Item;
|
|
||||||
|
|
||||||
// Register sheet application classes
|
|
||||||
Actors.unregisterSheet("core", ActorSheet);
|
|
||||||
Actors.registerSheet("ds4", DS4ActorSheet, { makeDefault: true });
|
|
||||||
Items.unregisterSheet("core", ItemSheet);
|
|
||||||
Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
|
|
||||||
|
|
||||||
registerHandlebarsPartials();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function registerHandlebarsPartials() {
|
|
||||||
const templatePaths = ["systems/ds4/templates/item/partials/description.hbs"];
|
|
||||||
return loadTemplates(templatePaths);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
/* Foundry VTT Setup */
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
|
||||||
*/
|
|
||||||
Hooks.once("setup", function () {
|
|
||||||
// Localize CONFIG objects once up-front
|
|
||||||
const toLocalize = ["attackTypes", "itemAvailabilities", "itemTypes", "armorTypes", "armorMaterialTypes"];
|
|
||||||
|
|
||||||
// Exclude some from sorting where the default order matters
|
|
||||||
const noSort = [];
|
|
||||||
|
|
||||||
// Localize and sort CONFIG objects
|
|
||||||
for (let o of toLocalize) {
|
|
||||||
const localized = Object.entries(CONFIG.DS4[o]).map((e) => {
|
|
||||||
return [e[0], game.i18n.localize(e[1])];
|
|
||||||
});
|
|
||||||
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
|
||||||
CONFIG.DS4[o] = localized.reduce((obj, e) => {
|
|
||||||
obj[e[0]] = e[1];
|
|
||||||
return obj;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,87 +0,0 @@
|
||||||
/**
|
|
||||||
* Extend the basic ItemSheet with some very simple modifications
|
|
||||||
* @extends {ItemSheet}
|
|
||||||
*/
|
|
||||||
export class DS4ItemSheet extends ItemSheet {
|
|
||||||
/** @override */
|
|
||||||
static get defaultOptions() {
|
|
||||||
return mergeObject(super.defaultOptions, {
|
|
||||||
width: 530,
|
|
||||||
height: 400,
|
|
||||||
classes: ["ds4", "sheet", "item"],
|
|
||||||
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
get template() {
|
|
||||||
const path = "systems/ds4/templates/item";
|
|
||||||
return `${path}/${this.item.data.type}-sheet.hbs`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
getData() {
|
|
||||||
const data = super.getData();
|
|
||||||
|
|
||||||
data.config = CONFIG.DS4;
|
|
||||||
console.log(data);
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
setPosition(options = {}) {
|
|
||||||
const position = super.setPosition(options);
|
|
||||||
const sheetBody = this.element.find(".sheet-body");
|
|
||||||
const bodyHeight = position.height - 192;
|
|
||||||
sheetBody.css("height", bodyHeight);
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
|
||||||
|
|
||||||
/** @override */
|
|
||||||
activateListeners(html) {
|
|
||||||
super.activateListeners(html);
|
|
||||||
|
|
||||||
if (!this.options.editable) return;
|
|
||||||
|
|
||||||
html.find(".effect-create").click(this._onEffectCreate.bind(this));
|
|
||||||
|
|
||||||
html.find(".effect-edit").click((ev) => {
|
|
||||||
const li = $(ev.currentTarget).parents(".effect");
|
|
||||||
console.log(li.data("effectId"));
|
|
||||||
const effect = this.item.effects.get(li.data("effectId"));
|
|
||||||
effect.sheet.render(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
html.find(".effect-delete").click(async (ev) => {
|
|
||||||
const li = $(ev.currentTarget).parents(".effect");
|
|
||||||
await this.item.deleteEmbeddedEntity("ActiveEffect", li.data("effectId"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle creating a new ActiveEffect for the item using initial data defined in the HTML dataset
|
|
||||||
* @param {Event} event The originating click event
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
async _onEffectCreate(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const label = `New Effect`;
|
|
||||||
|
|
||||||
const createData = {
|
|
||||||
label: label,
|
|
||||||
changes: [],
|
|
||||||
duration: {},
|
|
||||||
transfer: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const effect = ActiveEffect.create(createData, this.item);
|
|
||||||
return effect.create();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
/**
|
|
||||||
* Extend the basic Item with some very simple modifications.
|
|
||||||
* @extends {Item}
|
|
||||||
*/
|
|
||||||
export class DS4Item extends Item {
|
|
||||||
/**
|
|
||||||
* Augment the basic Item data model with additional dynamic data.
|
|
||||||
*/
|
|
||||||
prepareData() {
|
|
||||||
super.prepareData();
|
|
||||||
|
|
||||||
// Get the Item's data
|
|
||||||
const itemData = this.data;
|
|
||||||
const actorData = this.actor ? this.actor.data : {};
|
|
||||||
const data = itemData.data;
|
|
||||||
}
|
|
||||||
}
|
|
2167
package-lock.json
generated
2167
package-lock.json
generated
File diff suppressed because it is too large
Load diff
41
package.json
41
package.json
|
@ -1,23 +1,30 @@
|
||||||
{
|
{
|
||||||
"name": "ds4",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "CSS compiler for the Dungeonslayers 4 system",
|
|
||||||
"scripts": {
|
|
||||||
"build": "gulp",
|
|
||||||
"compile": "gulp css",
|
|
||||||
"watch": "gulp",
|
|
||||||
"gulp": "gulp"
|
|
||||||
},
|
|
||||||
"browserslist": [
|
|
||||||
"last 3 versions"
|
|
||||||
],
|
|
||||||
"author": "Saluu",
|
|
||||||
"license": "MIT",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"name": "test",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "",
|
||||||
|
"scripts": {
|
||||||
|
"package": "gulp package",
|
||||||
|
"build": "gulp build && gulp link",
|
||||||
|
"build:watch": "gulp watch",
|
||||||
|
"clean": "gulp clean && gulp link --clean",
|
||||||
|
"update": "npm install --save-dev gitlab:foundry-projects/foundry-pc/foundry-pc-types"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "",
|
||||||
|
"devDependencies": {
|
||||||
|
"archiver": "^5.1.0",
|
||||||
|
"chalk": "^4.1.0",
|
||||||
|
"foundry-pc-types": "gitlab:foundry-projects/foundry-pc/foundry-pc-types",
|
||||||
|
"fs-extra": "^9.0.1",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"gulp-autoprefixer": "^7.0.1",
|
"gulp-git": "^2.10.1",
|
||||||
|
"gulp-less": "^4.0.1",
|
||||||
"gulp-sass": "^4.1.0",
|
"gulp-sass": "^4.1.0",
|
||||||
"gulp-sourcemaps": "^2.6.5"
|
"gulp-typescript": "^6.0.0-alpha.1",
|
||||||
|
"json-stringify-pretty-compact": "^2.0.0",
|
||||||
|
"sass": "^1.30.0",
|
||||||
|
"typescript": "^4.1.3",
|
||||||
|
"yargs": "^16.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Import utilities.
|
|
||||||
@import "utils/typography";
|
|
||||||
@import "utils/colors";
|
|
||||||
@import "utils/mixins";
|
|
||||||
@import "utils/variables";
|
|
||||||
|
|
||||||
/* Global styles */
|
|
||||||
@import "global/window";
|
|
||||||
@import "global/grid";
|
|
||||||
@import "global/flex";
|
|
||||||
|
|
||||||
/* Styles limited to ds4 sheets */
|
|
||||||
.ds4 {
|
|
||||||
@import "components/apps";
|
|
||||||
@import "components/forms";
|
|
||||||
@import "components/basic_property";
|
|
||||||
@import "components/tabs";
|
|
||||||
@import "components/items";
|
|
||||||
@import "components/description";
|
|
||||||
}
|
|
20
src/ds4.scss
Normal file
20
src/ds4.scss
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// Import utilities.
|
||||||
|
@import "scss/utils/typography";
|
||||||
|
@import "scss/utils/colors";
|
||||||
|
@import "scss/utils/mixins";
|
||||||
|
@import "scss/utils/variables";
|
||||||
|
|
||||||
|
/* Global styles */
|
||||||
|
@import "scss/global/window";
|
||||||
|
@import "scss/global/grid";
|
||||||
|
@import "scss/global/flex";
|
||||||
|
|
||||||
|
/* Styles limited to ds4 sheets */
|
||||||
|
.ds4 {
|
||||||
|
@import "scss/components/apps";
|
||||||
|
@import "scss/components/forms";
|
||||||
|
@import "scss/components/basic_property";
|
||||||
|
@import "scss/components/tabs";
|
||||||
|
@import "scss/components/items";
|
||||||
|
@import "scss/components/description";
|
||||||
|
}
|
39
src/lang/en.json
Normal file
39
src/lang/en.json
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
"DS4.Description": "Description",
|
||||||
|
"DS4.Details": "Details",
|
||||||
|
"DS4.AttackType": "Attack Type",
|
||||||
|
"DS4.AttackTypeAbbr": "AT",
|
||||||
|
"DS4.WeaponBonus": "Weapon Bonus",
|
||||||
|
"DS4.WeaponBonusAbbr": "WB",
|
||||||
|
"DS4.OpponentDefense": "Opponent Defense",
|
||||||
|
"DS4.OpponentDefenseAbbr": "OD",
|
||||||
|
"DS4.AttackTypeMelee": "Melee",
|
||||||
|
"DS4.AttackTypeRanged": "Ranged",
|
||||||
|
"DS4.AttackTypeMeleeRanged": "Melee / Ranged",
|
||||||
|
"DS4.Quantity": "Quantity",
|
||||||
|
"DS4.PriceGold": "Price (Gold)",
|
||||||
|
"DS4.ItemAvailability": "Availability",
|
||||||
|
"DS4.ItemAvailabilityHamlet": "Hamlet",
|
||||||
|
"DS4.ItemAvailabilityVilage": "Village",
|
||||||
|
"DS4.ItemAvailabilityCity": "City",
|
||||||
|
"DS4.ItemAvailabilityElves": "Elves",
|
||||||
|
"DS4.ItemAvailabilityDwarves": "Dwarves",
|
||||||
|
"DS4.ItemAvailabilityNone": "None",
|
||||||
|
"DS4.ItemTypeWeapon": "Weapon",
|
||||||
|
"DS4.ItemTypeArmor": "Armor",
|
||||||
|
"DS4.ItemTypeShield": "Shield",
|
||||||
|
"DS4.ItemTypeTrinket": "Trinket",
|
||||||
|
"DS4.ItemTypeEquipment": "Equipment",
|
||||||
|
"DS4.ArmorType": "Armor Type",
|
||||||
|
"DS4.ArmorMaterialType": "Material Type",
|
||||||
|
"DS4.ArmorValue": "Armor Value",
|
||||||
|
"DS4.ArmorTypeBody": "Body",
|
||||||
|
"DS4.ArmorTypeHelmet": "Helmet",
|
||||||
|
"DS4.ArmorTypeVambrace": "Vambrace",
|
||||||
|
"DS4.ArmorTypeGreaves": "Greaves",
|
||||||
|
"DS4.ArmorTypeVambraceGreaves": "Vambrace + Greaves",
|
||||||
|
"DS4.ArmorMaterialTypeCloth": "Cloth",
|
||||||
|
"DS4.ArmorMaterialTypeLeather": "Leather",
|
||||||
|
"DS4.ArmorMaterialTypeChain": "Chain",
|
||||||
|
"DS4.ArmorMaterialTypePlate": "Plate"
|
||||||
|
}
|
110
src/module/actor/actor-sheet.ts
Normal file
110
src/module/actor/actor-sheet.ts
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/**
|
||||||
|
* Extend the basic ActorSheet with some very simple modifications
|
||||||
|
* @extends {ActorSheet}
|
||||||
|
*/
|
||||||
|
export class DS4ActorSheet extends ActorSheet<{
|
||||||
|
/* TODO: add actual type for data */
|
||||||
|
}> {
|
||||||
|
/** @override */
|
||||||
|
static get defaultOptions() {
|
||||||
|
return mergeObject(super.defaultOptions, {
|
||||||
|
classes: ["ds4", "sheet", "actor"],
|
||||||
|
template: "systems/ds4/templates/actor/actor-sheet.html",
|
||||||
|
width: 600,
|
||||||
|
height: 600,
|
||||||
|
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
getData() {
|
||||||
|
// TODO: replace ["..."] access with .
|
||||||
|
const data = super.getData();
|
||||||
|
data["dtypes"] = ["String", "Number", "Boolean"];
|
||||||
|
const innerData = data.data;
|
||||||
|
for (let attr of Object.values(data.data["attributes"])) {
|
||||||
|
attr["isCheckbox"] = attr["dtype"] === "Boolean";
|
||||||
|
}
|
||||||
|
console.log(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
activateListeners(html) {
|
||||||
|
super.activateListeners(html);
|
||||||
|
|
||||||
|
// Everything below here is only needed if the sheet is editable
|
||||||
|
if (!this.options.editable) return;
|
||||||
|
|
||||||
|
// Add Inventory Item
|
||||||
|
html.find(".item-create").click(this._onItemCreate.bind(this));
|
||||||
|
|
||||||
|
// Update Inventory Item
|
||||||
|
html.find(".item-edit").click((ev) => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
const item = this.actor.getOwnedItem(li.data("itemId"));
|
||||||
|
item.sheet.render(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete Inventory Item
|
||||||
|
html.find(".item-delete").click((ev) => {
|
||||||
|
const li = $(ev.currentTarget).parents(".item");
|
||||||
|
this.actor.deleteOwnedItem(li.data("itemId"));
|
||||||
|
li.slideUp(200, () => this.render(false));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rollable abilities.
|
||||||
|
html.find(".rollable").click(this._onRoll.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset
|
||||||
|
* @param {Event} event The originating click event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onItemCreate(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const header = event.currentTarget;
|
||||||
|
// Get the type of item to create.
|
||||||
|
const type = header.dataset.type;
|
||||||
|
// Grab any data associated with this control.
|
||||||
|
const data = duplicate(header.dataset);
|
||||||
|
// Initialize a default name.
|
||||||
|
const name = `New ${type.capitalize()}`;
|
||||||
|
// Prepare the item object.
|
||||||
|
const itemData = {
|
||||||
|
name: name,
|
||||||
|
type: type,
|
||||||
|
data: data,
|
||||||
|
};
|
||||||
|
// Remove the type from the dataset since it's in the itemData.type prop.
|
||||||
|
delete itemData.data["type"];
|
||||||
|
|
||||||
|
// Finally, create the item!
|
||||||
|
return this.actor.createOwnedItem(itemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle clickable rolls.
|
||||||
|
* @param {Event} event The originating click event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onRoll(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
const element = event.currentTarget;
|
||||||
|
const dataset = element.dataset;
|
||||||
|
|
||||||
|
if (dataset.roll) {
|
||||||
|
let roll = new Roll(dataset.roll, this.actor.data.data);
|
||||||
|
let label = dataset.label ? `Rolling ${dataset.label}` : "";
|
||||||
|
roll.roll().toMessage({
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
||||||
|
flavor: label,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/module/actor/actor.ts
Normal file
38
src/module/actor/actor.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
/** @override */
|
||||||
|
prepareDerivedData() {
|
||||||
|
const data = this.data;
|
||||||
|
this._prepareCombatValues(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
_prepareCombatValues(data) {
|
||||||
|
const hitPointsModifier = getProperty(data, "data.combatValues.hitPoints.modifier") || 0;
|
||||||
|
setProperty(
|
||||||
|
data,
|
||||||
|
"data.combatValues.hitPoints.max",
|
||||||
|
data.data.attributes.body.initial + data.data.traits.constitution.initial + 10 + hitPointsModifier
|
||||||
|
);
|
||||||
|
|
||||||
|
const defenseModifier = getProperty(data, "data.combatValues.defense.modifier") || 0;
|
||||||
|
setProperty(
|
||||||
|
data,
|
||||||
|
"data.combatValues.defense.value",
|
||||||
|
data.data.attributes.body.initial +
|
||||||
|
data.data.traits.constitution.initial +
|
||||||
|
this._getArmorValue() +
|
||||||
|
defenseModifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getArmorValue() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
68
src/module/config.ts
Normal file
68
src/module/config.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
export const DS4 = {
|
||||||
|
// ASCII Artwork
|
||||||
|
ASCII: `_____________________________________________________________________________________________
|
||||||
|
____ _ _ _ _ ____ _____ ___ _ _ ____ _ _ __ _______ ____ ____ _ _
|
||||||
|
| _ \\| | | | \\ | |/ ___| ____/ _ \\| \\ | / ___|| | / \\\\ \\ / / ____| _ \\/ ___| | || |
|
||||||
|
| | | | | | | \\| | | _| _|| | | | \\| \\___ \\| | / _ \\\\ V /| _| | |_) \\___ \\ | || |_
|
||||||
|
| |_| | |_| | |\\ | |_| | |__| |_| | |\\ |___) | |___ / ___ \\| | | |___| _ < ___) | |__ _|
|
||||||
|
|____/ \\___/|_| \\_|\\____|_____\\___/|_| \\_|____/|_____/_/ \\_\\_| |_____|_| \\_\\____/ |_|
|
||||||
|
=============================================================================================`,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the set of acttack types that can be performed with weapon items
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
attackTypes: {
|
||||||
|
melee: "DS4.AttackTypeMelee",
|
||||||
|
ranged: "DS4.AttackTypeRanged",
|
||||||
|
meleeRanged: "DS4.AttackTypeMeleeRanged",
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the set of item availabilties
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
itemAvailabilities: {
|
||||||
|
hamlet: "DS4.ItemAvailabilityHamlet",
|
||||||
|
village: "DS4.ItemAvailabilityVilage",
|
||||||
|
city: "DS4.ItemAvailabilityCity",
|
||||||
|
elves: "DS4.ItemAvailabilityElves",
|
||||||
|
dwarves: "DS4.ItemAvailabilityDwarves",
|
||||||
|
none: "DS4.ItemAvailabilityNone",
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * Define the set of item types
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
itemTypes: {
|
||||||
|
weapon: "DS4.ItemTypeWeapon",
|
||||||
|
armor: "DS4.ItemTypeArmor",
|
||||||
|
shield: "DS4.ItemTypeShield",
|
||||||
|
trinket: "DS4.ItemTypeTrinket",
|
||||||
|
equipment: "DS4.ItemTypeEquipment",
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * Define the set of armor types, a character may only wear one item of each at any given time
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
armorTypes: {
|
||||||
|
body: "DS4.ArmorTypeBody",
|
||||||
|
helment: "DS4.ArmorTypeHelmet",
|
||||||
|
vambrace: "DS4.ArmorTypeVambrace",
|
||||||
|
greaves: "DS4.ArmorTypeGreaves",
|
||||||
|
vambraceGreaves: "DS4.ArmorTypeVambraceGreaves",
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * Define the set of armor materials, used to determine if a characer may wear the armor without additional penalties
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
armorMaterialTypes: {
|
||||||
|
cloth: "DS4.ArmorMaterialTypeCloth",
|
||||||
|
leather: "DS4.ArmorMaterialTypeLeather",
|
||||||
|
chain: "DS4.ArmorMaterialTypeChain",
|
||||||
|
plate: "DS4.ArmorMaterialTypePlate",
|
||||||
|
},
|
||||||
|
};
|
63
src/module/ds4.ts
Normal file
63
src/module/ds4.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// Import Modules
|
||||||
|
import { DS4Actor } from "./actor/actor.js";
|
||||||
|
import { DS4ActorSheet } from "./actor/actor-sheet.js";
|
||||||
|
import { DS4Item } from "./item/item.js";
|
||||||
|
import { DS4ItemSheet } from "./item/item-sheet.js";
|
||||||
|
import { DS4 } from "./config.js";
|
||||||
|
|
||||||
|
Hooks.once("init", async function () {
|
||||||
|
console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`);
|
||||||
|
|
||||||
|
game.ds4 = {
|
||||||
|
DS4Actor,
|
||||||
|
DS4Item,
|
||||||
|
DS4,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Record configuration
|
||||||
|
CONFIG.DS4 = DS4;
|
||||||
|
|
||||||
|
// Define custom Entity classes
|
||||||
|
CONFIG.Actor.entityClass = DS4Actor;
|
||||||
|
CONFIG.Item.entityClass = DS4Item;
|
||||||
|
|
||||||
|
// Register sheet application classes
|
||||||
|
Actors.unregisterSheet("core", ActorSheet);
|
||||||
|
Actors.registerSheet("ds4", DS4ActorSheet, { makeDefault: true });
|
||||||
|
Items.unregisterSheet("core", ItemSheet);
|
||||||
|
Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true });
|
||||||
|
|
||||||
|
registerHandlebarsPartials();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function registerHandlebarsPartials() {
|
||||||
|
const templatePaths = ["systems/ds4/templates/item/partials/description.hbs"];
|
||||||
|
return loadTemplates(templatePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Foundry VTT Setup */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function runs after game data has been requested and loaded from the servers, so entities exist
|
||||||
|
*/
|
||||||
|
Hooks.once("setup", function () {
|
||||||
|
// Localize CONFIG objects once up-front
|
||||||
|
const toLocalize = ["attackTypes", "itemAvailabilities", "itemTypes", "armorTypes", "armorMaterialTypes"];
|
||||||
|
|
||||||
|
// Exclude some from sorting where the default order matters
|
||||||
|
const noSort = [];
|
||||||
|
|
||||||
|
// Localize and sort CONFIG objects
|
||||||
|
for (let o of toLocalize) {
|
||||||
|
const localized = Object.entries(CONFIG.DS4[o]).map((e) => {
|
||||||
|
return [e[0], game.i18n.localize(e[1] as string)];
|
||||||
|
});
|
||||||
|
if (!noSort.includes(o)) localized.sort((a, b) => a[1].localeCompare(b[1]));
|
||||||
|
CONFIG.DS4[o] = localized.reduce((obj, e) => {
|
||||||
|
obj[e[0]] = e[1];
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
});
|
85
src/module/item/item-sheet.ts
Normal file
85
src/module/item/item-sheet.ts
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
/**
|
||||||
|
* Extend the basic ItemSheet with some very simple modifications
|
||||||
|
* @extends {ItemSheet}
|
||||||
|
*/
|
||||||
|
export class DS4ItemSheet extends ItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static get defaultOptions() {
|
||||||
|
return mergeObject(super.defaultOptions, {
|
||||||
|
width: 530,
|
||||||
|
height: 400,
|
||||||
|
classes: ["ds4", "sheet", "item"],
|
||||||
|
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
get template() {
|
||||||
|
const path = "systems/ds4/templates/item";
|
||||||
|
return `${path}/${this.item.data.type}-sheet.hbs`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
getData() {
|
||||||
|
const data = { ...super.getData(), config: CONFIG.DS4 };
|
||||||
|
console.log(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
setPosition(options = {}) {
|
||||||
|
const position = super.setPosition(options);
|
||||||
|
const sheetBody = (this.element as JQuery).find(".sheet-body"); // TODO: Why is the cast necessary?
|
||||||
|
const bodyHeight = position.height - 192;
|
||||||
|
//sheetBody.css("height", bodyHeight);
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
activateListeners(html) {
|
||||||
|
super.activateListeners(html);
|
||||||
|
|
||||||
|
if (!this.options.editable) return;
|
||||||
|
|
||||||
|
html.find(".effect-create").click(this._onEffectCreate.bind(this));
|
||||||
|
|
||||||
|
html.find(".effect-edit").click((ev) => {
|
||||||
|
const li = $(ev.currentTarget).parents(".effect");
|
||||||
|
console.log(li.data("effectId"));
|
||||||
|
const effect = this.item.effects.get(li.data("effectId"));
|
||||||
|
effect.sheet.render(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.find(".effect-delete").click(async (ev) => {
|
||||||
|
const li = $(ev.currentTarget).parents(".effect");
|
||||||
|
await this.item.deleteEmbeddedEntity("ActiveEffect", li.data("effectId"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle creating a new ActiveEffect for the item using initial data defined in the HTML dataset
|
||||||
|
* @param {Event} event The originating click event
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
async _onEffectCreate(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const label = `New Effect`;
|
||||||
|
|
||||||
|
const createData = {
|
||||||
|
label: label,
|
||||||
|
changes: [],
|
||||||
|
duration: {},
|
||||||
|
transfer: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const effect = await ActiveEffect.create(createData, this.item);
|
||||||
|
return effect.create({});
|
||||||
|
}
|
||||||
|
}
|
17
src/module/item/item.ts
Normal file
17
src/module/item/item.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/**
|
||||||
|
* Extend the basic Item with some very simple modifications.
|
||||||
|
* @extends {Item}
|
||||||
|
*/
|
||||||
|
export class DS4Item extends Item {
|
||||||
|
/**
|
||||||
|
* Augment the basic Item data model with additional dynamic data.
|
||||||
|
*/
|
||||||
|
prepareData() {
|
||||||
|
super.prepareData();
|
||||||
|
|
||||||
|
// Get the Item's data
|
||||||
|
const itemData = this.data;
|
||||||
|
const actorData = this.actor ? this.actor.data : {};
|
||||||
|
const data = itemData.data;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@
|
||||||
font-family: "Wood Stamp";
|
font-family: "Wood Stamp";
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
src: local("Wood Stamp"), url("../fonts/Woodstamp.woff") format("woff");
|
src: local("Wood Stamp"), url("fonts/Woodstamp.woff") format("woff");
|
||||||
}
|
}
|
||||||
|
|
||||||
$font-primary: "Lora", sans-serif;
|
$font-primary: "Lora", sans-serif;
|
28
src/system.json
Normal file
28
src/system.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "ds4",
|
||||||
|
"title": "Dungeonslayers 4",
|
||||||
|
"description": "The Dungeonslayers 4 system for FoundryVTT!",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"minimumCoreVersion": "0.7.9",
|
||||||
|
"compatibleCoreVersion": "0.7.9",
|
||||||
|
"templateVersion": 2,
|
||||||
|
"author": "Johannes Loher",
|
||||||
|
"esmodules": ["module/ds4.js"],
|
||||||
|
"styles": ["ds4.css"],
|
||||||
|
"scripts": [],
|
||||||
|
"packs": [],
|
||||||
|
"languages": [
|
||||||
|
{
|
||||||
|
"lang": "en",
|
||||||
|
"name": "English",
|
||||||
|
"path": "lang/en.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"gridDistance": 1,
|
||||||
|
"gridUnits": "m",
|
||||||
|
"primaryTokenAttribute": "hitPoints",
|
||||||
|
"url": "",
|
||||||
|
"manifest": "",
|
||||||
|
"download": "",
|
||||||
|
"license": ""
|
||||||
|
}
|
79
src/template.json
Normal file
79
src/template.json
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
{
|
||||||
|
"Actor": {
|
||||||
|
"types": ["character"],
|
||||||
|
"templates": {},
|
||||||
|
"character": {
|
||||||
|
"templates": [],
|
||||||
|
"attributes": {
|
||||||
|
"body": {
|
||||||
|
"initial": 8
|
||||||
|
},
|
||||||
|
"mobility": {
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"mind": {
|
||||||
|
"initial": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"traits": {
|
||||||
|
"strength": {
|
||||||
|
"initial": 4
|
||||||
|
},
|
||||||
|
"constitution": {
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"agility": {
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"dexterity": {
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"intellect": {
|
||||||
|
"initial": 0
|
||||||
|
},
|
||||||
|
"aura": {
|
||||||
|
"initial": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Item": {
|
||||||
|
"types": ["weapon", "armor", "shield", "trinket", "equipment"],
|
||||||
|
"templates": {
|
||||||
|
"base": {
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"physical": {
|
||||||
|
"quantity": 1,
|
||||||
|
"price": 0,
|
||||||
|
"availability": "none"
|
||||||
|
},
|
||||||
|
"equipable": {
|
||||||
|
"equipped": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"weapon": {
|
||||||
|
"templates": ["base", "physical", "equipable"],
|
||||||
|
"attackType": "melee",
|
||||||
|
"weaponBonus": 0,
|
||||||
|
"opponentDefense": 0,
|
||||||
|
"properties": {}
|
||||||
|
},
|
||||||
|
"armor": {
|
||||||
|
"templates": ["base", "physical", "equipable"],
|
||||||
|
"armorMaterialType": "cloth",
|
||||||
|
"armorType": "body",
|
||||||
|
"armorValue": 0
|
||||||
|
},
|
||||||
|
"shield": {
|
||||||
|
"templates": ["base", "physical", "equipable"],
|
||||||
|
"armorValue": 0
|
||||||
|
},
|
||||||
|
"trinket": {
|
||||||
|
"templates": ["base", "physical", "equipable"]
|
||||||
|
},
|
||||||
|
"equipment": {
|
||||||
|
"templates": ["base", "physical"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
src/templates/actor/actor-sheet.html
Normal file
90
src/templates/actor/actor-sheet.html
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
<form class="{{cssClass}} flexcol" autocomplete="off">
|
||||||
|
{{!-- Sheet Header --}}
|
||||||
|
<header class="sheet-header">
|
||||||
|
<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>
|
||||||
|
{{!-- 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">
|
||||||
|
{{!-- "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>
|
||||||
|
</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>
|
||||||
|
</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>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{!-- Sheet Tab Navigation --}}
|
||||||
|
<nav class="sheet-tabs tabs" data-group="primary">
|
||||||
|
<a class="item" data-tab="description">Description</a>
|
||||||
|
<a class="item" data-tab="items">Items</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{{!-- Sheet Body --}}
|
||||||
|
<section class="sheet-body">
|
||||||
|
{{!-- Biography Tab --}}
|
||||||
|
<div class="tab biography" data-group="primary" data-tab="description">
|
||||||
|
{{editor content=data.biography target="data.biography" button=true owner=owner editable=editable}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Owned Items Tab --}}
|
||||||
|
<div class="tab items" data-group="primary" data-tab="items">
|
||||||
|
<ol class="items-list">
|
||||||
|
<li class="item flexrow item-header">
|
||||||
|
<div class="item-image"></div>
|
||||||
|
<div class="item-name">Name</div>
|
||||||
|
<div class="item-controls">
|
||||||
|
<a class="item-control item-create" title="Create item" data-type="weapon"
|
||||||
|
><i class="fas fa-plus"></i> Add item</a
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{#each actor.items as |item id|}}
|
||||||
|
<li class="item flexrow" data-item-id="{{item._id}}">
|
||||||
|
<div class="item-image">
|
||||||
|
<img src="{{item.img}}" title="{{item.name}}" width="24" height="24" />
|
||||||
|
</div>
|
||||||
|
<h4 class="item-name">{{item.name}}</h4>
|
||||||
|
<div class="item-controls">
|
||||||
|
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
|
||||||
|
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
28
system.json
28
system.json
|
@ -1,28 +0,0 @@
|
||||||
{
|
|
||||||
"name": "ds4",
|
|
||||||
"title": "Dungeonslayers 4",
|
|
||||||
"description": "The Dungeonslayers 4 system for FoundryVTT!",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"minimumCoreVersion": "0.7.4",
|
|
||||||
"compatibleCoreVersion": "0.7.4",
|
|
||||||
"templateVersion": 2,
|
|
||||||
"author": "Saluu",
|
|
||||||
"esmodules": ["module/ds4.js"],
|
|
||||||
"styles": ["css/ds4.css"],
|
|
||||||
"scripts": [],
|
|
||||||
"packs": [],
|
|
||||||
"languages": [
|
|
||||||
{
|
|
||||||
"lang": "en",
|
|
||||||
"name": "English",
|
|
||||||
"path": "lang/en.json"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"gridDistance": 1,
|
|
||||||
"gridUnits": "m",
|
|
||||||
"primaryTokenAttribute": "hitPoints",
|
|
||||||
"url": "",
|
|
||||||
"manifest": "",
|
|
||||||
"download": "",
|
|
||||||
"license": "LICENSE.txt"
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
{
|
|
||||||
"Actor": {
|
|
||||||
"types": ["character"],
|
|
||||||
"templates": {},
|
|
||||||
"character": {
|
|
||||||
"templates": [],
|
|
||||||
"attributes": {
|
|
||||||
"body": {
|
|
||||||
"initial": 8
|
|
||||||
},
|
|
||||||
"mobility": {
|
|
||||||
"initial": 0
|
|
||||||
},
|
|
||||||
"mind": {
|
|
||||||
"initial": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"traits": {
|
|
||||||
"strength": {
|
|
||||||
"initial": 4
|
|
||||||
},
|
|
||||||
"constitution": {
|
|
||||||
"initial": 0
|
|
||||||
},
|
|
||||||
"agility": {
|
|
||||||
"initial": 0
|
|
||||||
},
|
|
||||||
"dexterity": {
|
|
||||||
"initial": 0
|
|
||||||
},
|
|
||||||
"intellect": {
|
|
||||||
"initial": 0
|
|
||||||
},
|
|
||||||
"aura": {
|
|
||||||
"initial": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Item": {
|
|
||||||
"types": ["weapon", "armor", "shield", "trinket", "equipment"],
|
|
||||||
"templates": {
|
|
||||||
"base": {
|
|
||||||
"description": ""
|
|
||||||
},
|
|
||||||
"physical": {
|
|
||||||
"quantity": 1,
|
|
||||||
"price": 0,
|
|
||||||
"availability": "none"
|
|
||||||
},
|
|
||||||
"equipable": {
|
|
||||||
"equipped": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"weapon": {
|
|
||||||
"templates": ["base", "physical", "equipable"],
|
|
||||||
"attackType": "melee",
|
|
||||||
"weaponBonus": 0,
|
|
||||||
"opponentDefense": 0,
|
|
||||||
"properties": {}
|
|
||||||
},
|
|
||||||
"armor": {
|
|
||||||
"templates": ["base", "physical", "equipable"],
|
|
||||||
"armorMaterialType": "cloth",
|
|
||||||
"armorType": "body",
|
|
||||||
"armorValue": 0
|
|
||||||
},
|
|
||||||
"shield": {
|
|
||||||
"templates": ["base", "physical", "equipable"],
|
|
||||||
"armorValue": 0
|
|
||||||
},
|
|
||||||
"trinket": {
|
|
||||||
"templates": ["base", "physical", "equipable"]
|
|
||||||
},
|
|
||||||
"equipment": {
|
|
||||||
"templates": ["base", "physical"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
<form class="{{cssClass}} flexcol" autocomplete="off">
|
|
||||||
|
|
||||||
{{!-- Sheet Header --}}
|
|
||||||
<header class="sheet-header">
|
|
||||||
<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>
|
|
||||||
{{!-- 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">
|
|
||||||
{{!-- "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>
|
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{{!-- Sheet Tab Navigation --}}
|
|
||||||
<nav class="sheet-tabs tabs" data-group="primary">
|
|
||||||
<a class="item" data-tab="description">Description</a>
|
|
||||||
<a class="item" data-tab="items">Items</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
{{!-- Sheet Body --}}
|
|
||||||
<section class="sheet-body">
|
|
||||||
|
|
||||||
{{!-- Biography Tab --}}
|
|
||||||
<div class="tab biography" data-group="primary" data-tab="description">
|
|
||||||
{{editor content=data.biography target="data.biography" button=true owner=owner editable=editable}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{!-- Owned Items Tab --}}
|
|
||||||
<div class="tab items" data-group="primary" data-tab="items">
|
|
||||||
<ol class="items-list">
|
|
||||||
<li class="item flexrow item-header">
|
|
||||||
<div class="item-image"></div>
|
|
||||||
<div class="item-name">Name</div>
|
|
||||||
<div class="item-controls">
|
|
||||||
<a class="item-control item-create" title="Create item" data-type="weapon"><i class="fas fa-plus"></i> Add item</a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{{#each actor.items as |item id|}}
|
|
||||||
<li class="item flexrow" data-item-id="{{item._id}}">
|
|
||||||
<div class="item-image"><img src="{{item.img}}" title="{{item.name}}" width="24" height="24"/></div>
|
|
||||||
<h4 class="item-name">{{item.name}}</h4>
|
|
||||||
<div class="item-controls">
|
|
||||||
<a class="item-control item-edit" title="Edit Item"><i class="fas fa-edit"></i></a>
|
|
||||||
<a class="item-control item-delete" title="Delete Item"><i class="fas fa-trash"></i></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
</form>
|
|
||||||
|
|
11
tsconfig.json
Normal file
11
tsconfig.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"ES6",
|
||||||
|
"ES2017"
|
||||||
|
],
|
||||||
|
"types": ["foundry-pc-types"]
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue