Initial skeleton
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules/
|
||||||
|
css/
|
||||||
|
packs/
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,72 @@
|
|||||||
|
import { SYSTEM } from "./module/config/system.mjs"
|
||||||
|
globalThis.SYSTEM = SYSTEM
|
||||||
|
|
||||||
|
import * as models from "./module/models/_module.mjs"
|
||||||
|
import * as documents from "./module/documents/_module.mjs"
|
||||||
|
import * as applications from "./module/applications/_module.mjs"
|
||||||
|
|
||||||
|
Hooks.once("init", function () {
|
||||||
|
console.info("Adventures with Emmy | Initializing System")
|
||||||
|
console.info(SYSTEM.ASCII)
|
||||||
|
|
||||||
|
globalThis.adventuresWithEmmy = game.system
|
||||||
|
game.system.CONST = SYSTEM
|
||||||
|
|
||||||
|
game.system.api = { applications, models, documents }
|
||||||
|
|
||||||
|
CONFIG.Actor.documentClass = documents.AwEActor
|
||||||
|
CONFIG.Actor.dataModels = {
|
||||||
|
character: models.AwECharacter,
|
||||||
|
creature: models.AwECreature
|
||||||
|
}
|
||||||
|
|
||||||
|
CONFIG.Item.documentClass = documents.AwEItem
|
||||||
|
CONFIG.Item.dataModels = {
|
||||||
|
ability: models.AwEAbility,
|
||||||
|
field: models.AwEField,
|
||||||
|
archetype: models.AwEArchetype,
|
||||||
|
background: models.AwEBackground,
|
||||||
|
kit: models.AwEKit,
|
||||||
|
weapon: models.AwEWeapon,
|
||||||
|
equipment: models.AwEEquipment
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register actor sheets
|
||||||
|
foundry.documents.collections.Actors.unregisterSheet("core", foundry.appv1.sheets.ActorSheet)
|
||||||
|
foundry.documents.collections.Actors.registerSheet("fvtt-adventures-with-emmy", applications.AwECharacterSheet, {
|
||||||
|
types: ["character"], makeDefault: true
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Actors.registerSheet("fvtt-adventures-with-emmy", applications.AwECreatureSheet, {
|
||||||
|
types: ["creature"], makeDefault: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// Register item sheets
|
||||||
|
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1.sheets.ItemSheet)
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEAbilitySheet, {
|
||||||
|
types: ["ability"], makeDefault: true
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEFieldSheet, {
|
||||||
|
types: ["field"], makeDefault: true
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEArchetypeSheet, {
|
||||||
|
types: ["archetype"], makeDefault: true
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEBackgroundSheet, {
|
||||||
|
types: ["background"], makeDefault: true
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEKitSheet, {
|
||||||
|
types: ["kit"], makeDefault: true
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEWeaponSheet, {
|
||||||
|
types: ["weapon"], makeDefault: true
|
||||||
|
})
|
||||||
|
foundry.documents.collections.Items.registerSheet("fvtt-adventures-with-emmy", applications.AwEEquipmentSheet, {
|
||||||
|
types: ["equipment"], makeDefault: true
|
||||||
|
})
|
||||||
|
|
||||||
|
CONFIG.ChatMessage.documentClass = documents.AwEChatMessage
|
||||||
|
})
|
||||||
|
|
||||||
|
Hooks.once("ready", function () {
|
||||||
|
console.info("Adventures with Emmy | System Ready")
|
||||||
|
})
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
import jsdoc from 'eslint-plugin-jsdoc';
|
||||||
|
import prettier from 'eslint-plugin-prettier';
|
||||||
|
import configPrettier from 'eslint-config-prettier';
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
ignores: [
|
||||||
|
"node_modules/",
|
||||||
|
"eslint.config.mjs",
|
||||||
|
"build.mjs",
|
||||||
|
"gulpfile.js"
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
browser: true,
|
||||||
|
es2022: true,
|
||||||
|
node: true,
|
||||||
|
jquery: true,
|
||||||
|
},
|
||||||
|
ecmaVersion: 2022,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
jsdoc,
|
||||||
|
prettier
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'array-bracket-spacing': ['warn', 'never'],
|
||||||
|
'array-callback-return': 'warn',
|
||||||
|
'arrow-spacing': 'warn',
|
||||||
|
'comma-dangle': ['warn', 'never'],
|
||||||
|
'comma-style': 'warn',
|
||||||
|
'computed-property-spacing': 'warn',
|
||||||
|
'constructor-super': 'error',
|
||||||
|
'default-param-last': 'warn',
|
||||||
|
'dot-location': ['warn', 'property'],
|
||||||
|
'eol-last': ['error', 'always'],
|
||||||
|
'eqeqeq': ['warn', 'smart'],
|
||||||
|
'func-call-spacing': 'warn',
|
||||||
|
'func-names': ['warn', 'never'],
|
||||||
|
'getter-return': 'warn',
|
||||||
|
'lines-between-class-members': 'warn',
|
||||||
|
'new-parens': ['warn', 'always'],
|
||||||
|
'no-alert': 'warn',
|
||||||
|
'no-array-constructor': 'warn',
|
||||||
|
'no-class-assign': 'warn',
|
||||||
|
'no-compare-neg-zero': 'warn',
|
||||||
|
'no-cond-assign': 'warn',
|
||||||
|
'no-const-assign': 'error',
|
||||||
|
'no-constant-condition': 'warn',
|
||||||
|
'no-constructor-return': 'warn',
|
||||||
|
'no-delete-var': 'warn',
|
||||||
|
'no-dupe-args': 'warn',
|
||||||
|
'no-dupe-class-members': 'warn',
|
||||||
|
'no-dupe-keys': 'warn',
|
||||||
|
'no-duplicate-case': 'warn',
|
||||||
|
'no-duplicate-imports': ['warn', { includeExports: true }],
|
||||||
|
'no-empty': ['warn', { allowEmptyCatch: true }],
|
||||||
|
'no-empty-character-class': 'warn',
|
||||||
|
'no-empty-pattern': 'warn',
|
||||||
|
'no-func-assign': 'warn',
|
||||||
|
'no-global-assign': 'warn',
|
||||||
|
'no-implicit-coercion': ['warn', { allow: ['!!'] }],
|
||||||
|
'no-implied-eval': 'warn',
|
||||||
|
'no-import-assign': 'warn',
|
||||||
|
'no-invalid-regexp': 'warn',
|
||||||
|
'no-irregular-whitespace': 'warn',
|
||||||
|
'no-iterator': 'warn',
|
||||||
|
'no-lone-blocks': 'warn',
|
||||||
|
'no-lonely-if': 'off',
|
||||||
|
'no-loop-func': 'warn',
|
||||||
|
'no-misleading-character-class': 'warn',
|
||||||
|
'no-mixed-operators': 'warn',
|
||||||
|
'no-multi-str': 'warn',
|
||||||
|
'no-multiple-empty-lines': 'warn',
|
||||||
|
'no-new-func': 'warn',
|
||||||
|
'no-new-object': 'warn',
|
||||||
|
'no-new-symbol': 'warn',
|
||||||
|
'no-new-wrappers': 'warn',
|
||||||
|
'no-nonoctal-decimal-escape': 'warn',
|
||||||
|
'no-obj-calls': 'warn',
|
||||||
|
'no-octal': 'warn',
|
||||||
|
'no-octal-escape': 'warn',
|
||||||
|
'no-promise-executor-return': 'warn',
|
||||||
|
'no-proto': 'warn',
|
||||||
|
'no-regex-spaces': 'warn',
|
||||||
|
'no-script-url': 'warn',
|
||||||
|
'no-self-assign': 'warn',
|
||||||
|
'no-self-compare': 'warn',
|
||||||
|
'no-setter-return': 'warn',
|
||||||
|
'no-sequences': 'warn',
|
||||||
|
'no-template-curly-in-string': 'warn',
|
||||||
|
'no-this-before-super': 'error',
|
||||||
|
'no-unexpected-multiline': 'warn',
|
||||||
|
'no-unmodified-loop-condition': 'warn',
|
||||||
|
'no-unneeded-ternary': 'warn',
|
||||||
|
'no-unreachable': 'warn',
|
||||||
|
'no-unreachable-loop': 'warn',
|
||||||
|
'no-unsafe-negation': ['warn', { enforceForOrderingRelations: true }],
|
||||||
|
'no-unsafe-optional-chaining': ['warn', { disallowArithmeticOperators: true }],
|
||||||
|
'no-unused-expressions': 'warn',
|
||||||
|
'no-useless-backreference': 'warn',
|
||||||
|
'no-useless-call': 'warn',
|
||||||
|
'no-useless-catch': 'warn',
|
||||||
|
'no-useless-computed-key': ['warn', { enforceForClassMembers: true }],
|
||||||
|
'no-useless-concat': 'warn',
|
||||||
|
'no-useless-constructor': 'warn',
|
||||||
|
'no-useless-rename': 'warn',
|
||||||
|
'no-useless-return': 'warn',
|
||||||
|
'no-var': 'warn',
|
||||||
|
'no-void': 'warn',
|
||||||
|
'no-whitespace-before-property': 'warn',
|
||||||
|
'prefer-numeric-literals': 'warn',
|
||||||
|
'prefer-object-spread': 'warn',
|
||||||
|
'prefer-regex-literals': 'warn',
|
||||||
|
'prefer-spread': 'warn',
|
||||||
|
'rest-spread-spacing': ['warn', 'never'],
|
||||||
|
'semi-spacing': 'warn',
|
||||||
|
'semi-style': ['warn', 'last'],
|
||||||
|
'space-unary-ops': ['warn', { words: true, nonwords: false }],
|
||||||
|
'switch-colon-spacing': 'warn',
|
||||||
|
'symbol-description': 'warn',
|
||||||
|
'template-curly-spacing': ['warn', 'never'],
|
||||||
|
'unicode-bom': ['warn', 'never'],
|
||||||
|
'use-isnan': ['warn', { enforceForSwitchCase: true, enforceForIndexOf: true }],
|
||||||
|
'valid-typeof': ['warn', { requireStringLiterals: true }],
|
||||||
|
'wrap-iife': ['warn', 'inside'],
|
||||||
|
'arrow-parens': ['warn', 'as-needed', { requireForBlockBody: false }],
|
||||||
|
'capitalized-comments': ['warn', 'always', {
|
||||||
|
ignoreConsecutiveComments: true,
|
||||||
|
ignorePattern: 'noinspection',
|
||||||
|
}],
|
||||||
|
'comma-spacing': 'warn',
|
||||||
|
'dot-notation': 'warn',
|
||||||
|
indent: ['warn', 2, { SwitchCase: 1 }],
|
||||||
|
'key-spacing': 'warn',
|
||||||
|
'keyword-spacing': ['warn', { overrides: { catch: { before: true, after: false } } }],
|
||||||
|
'max-len': ['warn', {
|
||||||
|
code: 180,
|
||||||
|
ignoreTrailingComments: true,
|
||||||
|
ignoreUrls: true,
|
||||||
|
ignoreStrings: true,
|
||||||
|
ignoreTemplateLiterals: true,
|
||||||
|
}],
|
||||||
|
'no-extra-boolean-cast': ['warn', { enforceForLogicalOperands: true }],
|
||||||
|
'no-multi-spaces': ['warn', { ignoreEOLComments: true }],
|
||||||
|
'no-tabs': 'warn',
|
||||||
|
'no-throw-literal': 'error',
|
||||||
|
'no-trailing-spaces': 'warn',
|
||||||
|
'no-useless-escape': 'warn',
|
||||||
|
'nonblock-statement-body-position': ['warn', 'beside'],
|
||||||
|
'one-var': ['warn', 'never'],
|
||||||
|
'operator-linebreak': ['warn', 'before', {
|
||||||
|
overrides: { '=': 'after', '+=': 'after', '-=': 'after' },
|
||||||
|
}],
|
||||||
|
'prefer-template': 'warn',
|
||||||
|
'quote-props': ['warn', 'as-needed', { keywords: false }],
|
||||||
|
quotes: ['warn', 'double', { avoidEscape: true, allowTemplateLiterals: false }],
|
||||||
|
semi: ["error", "never"],
|
||||||
|
'spaced-comment': 'warn',
|
||||||
|
'jsdoc/check-access': 'warn',
|
||||||
|
'jsdoc/check-alignment': 'warn',
|
||||||
|
'jsdoc/check-examples': 'off',
|
||||||
|
'jsdoc/check-indentation': 'off',
|
||||||
|
'jsdoc/check-line-alignment': 'off',
|
||||||
|
'jsdoc/check-param-names': 'warn',
|
||||||
|
'jsdoc/check-property-names': 'warn',
|
||||||
|
'jsdoc/check-syntax': 'off',
|
||||||
|
'jsdoc/check-tag-names': ['warn', { definedTags: ['category'] }],
|
||||||
|
'jsdoc/check-types': 'warn',
|
||||||
|
'jsdoc/check-values': 'warn',
|
||||||
|
'jsdoc/empty-tags': 'warn',
|
||||||
|
'jsdoc/implements-on-classes': 'warn',
|
||||||
|
'jsdoc/match-description': 'off',
|
||||||
|
'jsdoc/newline-after-description': 'off',
|
||||||
|
'jsdoc/no-bad-blocks': 'warn',
|
||||||
|
'jsdoc/no-defaults': 'off',
|
||||||
|
'jsdoc/no-types': 'off',
|
||||||
|
'jsdoc/no-undefined-types': 'off',
|
||||||
|
'jsdoc/require-description': 'warn',
|
||||||
|
'jsdoc/require-description-complete-sentence': 'off',
|
||||||
|
'jsdoc/require-example': 'off',
|
||||||
|
'jsdoc/require-file-overview': 'off',
|
||||||
|
'jsdoc/require-hyphen-before-param-description': ['warn', 'never'],
|
||||||
|
'jsdoc/require-jsdoc': 'warn',
|
||||||
|
'jsdoc/require-param': 'warn',
|
||||||
|
'jsdoc/require-param-description': 'off',
|
||||||
|
'jsdoc/require-param-name': 'warn',
|
||||||
|
'jsdoc/require-param-type': 'warn',
|
||||||
|
'jsdoc/require-property': 'warn',
|
||||||
|
'jsdoc/require-property-description': 'off',
|
||||||
|
'jsdoc/require-property-name': 'warn',
|
||||||
|
'jsdoc/require-property-type': 'warn',
|
||||||
|
'jsdoc/require-returns': 'off',
|
||||||
|
'jsdoc/require-returns-check': 'warn',
|
||||||
|
'jsdoc/require-returns-description': 'off',
|
||||||
|
'jsdoc/require-returns-type': 'warn',
|
||||||
|
'jsdoc/require-throws': 'off',
|
||||||
|
'jsdoc/require-yields': 'warn',
|
||||||
|
'jsdoc/require-yields-check': 'warn',
|
||||||
|
'jsdoc/valid-types': 'off',
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
jsdoc: {
|
||||||
|
preferredTypes: {
|
||||||
|
'.<>': '<>',
|
||||||
|
object: 'Object',
|
||||||
|
Object: 'object',
|
||||||
|
},
|
||||||
|
mode: 'typescript',
|
||||||
|
tagNamePreference: {
|
||||||
|
augments: 'extends',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Ajout de la configuration Prettier qui désactive les règles ESLint en conflit avec Prettier
|
||||||
|
configPrettier
|
||||||
|
];
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
const gulp = require('gulp');
|
||||||
|
const less = require('gulp-less');
|
||||||
|
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
/* Compile LESS
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
function compileLESS() {
|
||||||
|
return gulp.src("styles/adventures-with-emmy.less")
|
||||||
|
.pipe(less())
|
||||||
|
.pipe(gulp.dest("./css"))
|
||||||
|
}
|
||||||
|
const css = gulp.series(compileLESS);
|
||||||
|
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
/* Watch Updates
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
const SIMPLE_LESS = ["styles/*.less"];
|
||||||
|
|
||||||
|
function watchUpdates() {
|
||||||
|
gulp.watch(SIMPLE_LESS, css);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
/* Export Tasks
|
||||||
|
/* ----------------------------------------- */
|
||||||
|
|
||||||
|
exports.default = gulp.series(
|
||||||
|
gulp.parallel(css),
|
||||||
|
watchUpdates
|
||||||
|
);
|
||||||
|
exports.css = css;
|
||||||
|
exports.watchUpdates = watchUpdates;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"AWEMMY.Actor.Character": "Hero",
|
||||||
|
"AWEMMY.Actor.Creature": "Creature",
|
||||||
|
"AWEMMY.Attribute.Agility": "Agility",
|
||||||
|
"AWEMMY.Attribute.Fitness": "Fitness",
|
||||||
|
"AWEMMY.Attribute.Awareness": "Awareness",
|
||||||
|
"AWEMMY.Attribute.Influence": "Influence",
|
||||||
|
"AWEMMY.Attribute.AGI": "AGI",
|
||||||
|
"AWEMMY.Attribute.FIT": "FIT",
|
||||||
|
"AWEMMY.Attribute.AWA": "AWA",
|
||||||
|
"AWEMMY.Attribute.INF": "INF",
|
||||||
|
"AWEMMY.Condition.Edge": "Edge",
|
||||||
|
"AWEMMY.Condition.Hampered": "Hampered",
|
||||||
|
"AWEMMY.Condition.Inhibited": "Inhibited",
|
||||||
|
"AWEMMY.Condition.Jumbled": "Jumbled",
|
||||||
|
"AWEMMY.Condition.Mishap": "Mishap",
|
||||||
|
"AWEMMY.Condition.Prone": "Prone",
|
||||||
|
"AWEMMY.Condition.Quickened": "Quickened",
|
||||||
|
"AWEMMY.Condition.Slowed": "Slowed",
|
||||||
|
"AWEMMY.Condition.Vulnerable": "Vulnerable",
|
||||||
|
"AWEMMY.Ability.Cost.Free": "Free",
|
||||||
|
"AWEMMY.Ability.Type.Field": "Field",
|
||||||
|
"AWEMMY.Ability.Type.Archetype": "Archetype",
|
||||||
|
"AWEMMY.Ability.Type.General": "General",
|
||||||
|
"AWEMMY.Ability.Type.Beginner": "Beginner",
|
||||||
|
"AWEMMY.Item.Ability": "Ability",
|
||||||
|
"AWEMMY.Item.Field": "Field",
|
||||||
|
"AWEMMY.Item.Archetype": "Archetype",
|
||||||
|
"AWEMMY.Item.Background": "Background",
|
||||||
|
"AWEMMY.Item.Kit": "Kit",
|
||||||
|
"AWEMMY.Item.Weapon": "Weapon",
|
||||||
|
"AWEMMY.Item.Equipment": "Equipment",
|
||||||
|
"AWEMMY.Roll.CriticalSuccess": "Critical Success",
|
||||||
|
"AWEMMY.Roll.Success": "Success",
|
||||||
|
"AWEMMY.Roll.Failure": "Failure",
|
||||||
|
"AWEMMY.Roll.CriticalFailure": "Critical Failure",
|
||||||
|
"AWEMMY.Sheet.Tab.Main": "Main",
|
||||||
|
"AWEMMY.Sheet.Tab.Biography": "Biography",
|
||||||
|
"AWEMMY.Sheet.Tab.Equipment": "Equipment",
|
||||||
|
"AWEMMY.Sheet.EditMode": "Edit",
|
||||||
|
"AWEMMY.Sheet.PlayMode": "Play",
|
||||||
|
"AWEMMY.Character.Level": "Level",
|
||||||
|
"AWEMMY.Character.Stride": "Stride",
|
||||||
|
"AWEMMY.Character.HP": "HP",
|
||||||
|
"AWEMMY.Character.FlowPoints": "Flow Points",
|
||||||
|
"AWEMMY.Character.BoostLevel": "Boost Level",
|
||||||
|
"AWEMMY.Character.Mod": "MOD",
|
||||||
|
"AWEMMY.Character.DC": "DC",
|
||||||
|
"AWEMMY.Creature.EurekaRubric": "Eureka Rubric",
|
||||||
|
"AWEMMY.Creature.Claims": "Claims",
|
||||||
|
"AWEMMY.Creature.Evidence": "Evidence",
|
||||||
|
"AWEMMY.Creature.Hints": "Hints",
|
||||||
|
"AWEMMY.Ability.Cost": "Cost",
|
||||||
|
"AWEMMY.Ability.Frequency": "Frequency",
|
||||||
|
"AWEMMY.Ability.Requirements": "Requirements",
|
||||||
|
"AWEMMY.Ability.Trigger": "Trigger",
|
||||||
|
"AWEMMY.Ability.Traits": "Traits",
|
||||||
|
"AWEMMY.Weapon.Range": "Range",
|
||||||
|
"AWEMMY.Weapon.Damage": "Damage",
|
||||||
|
"AWEMMY.Weapon.DamageType": "Damage Type",
|
||||||
|
"AWEMMY.Weapon.AttackAttribute": "Attack Attribute",
|
||||||
|
"AWEMMY.Field.KeyAttribute": "Key Attribute",
|
||||||
|
"AWEMMY.Field.Specializations": "Specializations",
|
||||||
|
"AWEMMY.Field.KnowledgeBonus": "Knowledge Bonus",
|
||||||
|
"AWEMMY.Background.Bonus": "Background Bonus",
|
||||||
|
"AWEMMY.Kit.Field": "Field",
|
||||||
|
"AWEMMY.Kit.Charges": "Charges",
|
||||||
|
"AWEMMY.Equipment.Quantity": "Quantity",
|
||||||
|
"AWEMMY.Equipment.Weight": "Weight"
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export { default as AwECharacterSheet } from "./sheets/character-sheet.mjs"
|
||||||
|
export { default as AwECreatureSheet } from "./sheets/creature-sheet.mjs"
|
||||||
|
export { default as AwEAbilitySheet } from "./sheets/ability-sheet.mjs"
|
||||||
|
export { default as AwEFieldSheet } from "./sheets/field-sheet.mjs"
|
||||||
|
export { default as AwEArchetypeSheet } from "./sheets/archetype-sheet.mjs"
|
||||||
|
export { default as AwEBackgroundSheet } from "./sheets/background-sheet.mjs"
|
||||||
|
export { default as AwEKitSheet } from "./sheets/kit-sheet.mjs"
|
||||||
|
export { default as AwEWeaponSheet } from "./sheets/weapon-sheet.mjs"
|
||||||
|
export { default as AwEEquipmentSheet } from "./sheets/equipment-sheet.mjs"
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import AwEItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwEAbilitySheet extends AwEItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["ability"],
|
||||||
|
position: { width: 620 },
|
||||||
|
window: { contentClasses: ["ability-content"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/ability.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import AwEItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwEArchetypeSheet extends AwEItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["archetype"],
|
||||||
|
position: { width: 620 },
|
||||||
|
window: { contentClasses: ["archetype-content"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/archetype.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import AwEItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwEBackgroundSheet extends AwEItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["background"],
|
||||||
|
position: { width: 620 },
|
||||||
|
window: { contentClasses: ["background-content"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/background.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,231 @@
|
|||||||
|
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||||
|
|
||||||
|
export default class AwEActorSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ActorSheetV2) {
|
||||||
|
/**
|
||||||
|
* Different sheet modes.
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||||
|
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options)
|
||||||
|
this.#dragDrop = this.#createDragDropHandlers()
|
||||||
|
}
|
||||||
|
|
||||||
|
#dragDrop
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["awemmy", "actor"],
|
||||||
|
position: {
|
||||||
|
width: 900,
|
||||||
|
height: "auto"
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
submitOnChange: true
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
resizable: true
|
||||||
|
},
|
||||||
|
dragDrop: [{ dragSelector: '[data-drag="true"], .rollable', dropSelector: null }],
|
||||||
|
actions: {
|
||||||
|
editImage: AwEActorSheet.#onEditImage,
|
||||||
|
toggleSheet: AwEActorSheet.#onToggleSheet,
|
||||||
|
edit: AwEActorSheet.#onItemEdit,
|
||||||
|
delete: AwEActorSheet.#onItemDelete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current sheet mode.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
_sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the sheet currently in 'Play' mode?
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isPlayMode() {
|
||||||
|
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the sheet currently in 'Edit' mode?
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isEditMode() {
|
||||||
|
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = {
|
||||||
|
fields: this.document.schema.fields,
|
||||||
|
systemFields: this.document.system.schema.fields,
|
||||||
|
actor: this.document,
|
||||||
|
system: this.document.system,
|
||||||
|
source: this.document.toObject(),
|
||||||
|
isEditMode: this.isEditMode,
|
||||||
|
isPlayMode: this.isPlayMode,
|
||||||
|
isEditable: this.isEditable
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onRender(context, options) {
|
||||||
|
this.#dragDrop.forEach(d => d.bind(this.element))
|
||||||
|
const rollables = this.element.querySelectorAll(".rollable")
|
||||||
|
rollables.forEach(d => d.addEventListener("click", this._onRoll.bind(this)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle rolling an attribute check.
|
||||||
|
* @param {PointerEvent} event - The click event.
|
||||||
|
*/
|
||||||
|
async _onRoll(event) {
|
||||||
|
if (this.isEditMode) return
|
||||||
|
const attributeId = event.currentTarget.dataset.attributeId
|
||||||
|
if (!attributeId) return
|
||||||
|
await this.document.rollAttribute(attributeId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Drag-and-Drop Workflow
|
||||||
|
/**
|
||||||
|
* Create drag-and-drop workflow handlers for this Application.
|
||||||
|
* @returns {DragDrop[]} An array of DragDrop handlers.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#createDragDropHandlers() {
|
||||||
|
return this.options.dragDrop.map(d => {
|
||||||
|
d.permissions = {
|
||||||
|
dragstart: this._canDragStart.bind(this),
|
||||||
|
drop: this._canDragDrop.bind(this)
|
||||||
|
}
|
||||||
|
d.callbacks = {
|
||||||
|
dragstart: this._onDragStart.bind(this),
|
||||||
|
dragover: this._onDragOver.bind(this),
|
||||||
|
drop: this._onDrop.bind(this)
|
||||||
|
}
|
||||||
|
return new foundry.applications.ux.DragDrop.implementation(d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur when a dragged element is dropped on a target.
|
||||||
|
* @param {DragEvent} event - The originating DragEvent.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
async _onDrop(event) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define whether a user is able to begin a dragstart workflow for a given drag selector.
|
||||||
|
* @param {string} selector - The candidate HTML selector for dragging.
|
||||||
|
* @returns {boolean} Can the current user drag this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragStart(selector) {
|
||||||
|
return this.isEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define whether a user is able to conclude a drag-and-drop workflow for a given drop selector.
|
||||||
|
* @param {string} selector - The candidate HTML selector for the drop target.
|
||||||
|
* @returns {boolean} Can the current user drop on this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragDrop(selector) {
|
||||||
|
return this.isEditable && this.document.isOwner
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur at the beginning of a drag start workflow.
|
||||||
|
* @param {DragEvent} event - The originating DragEvent.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDragStart(event) {
|
||||||
|
if ("link" in event.target.dataset) return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur when a dragged element is over a drop target.
|
||||||
|
* @param {DragEvent} event - The originating DragEvent.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDragOver(event) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop an item onto the actor.
|
||||||
|
* @param {Item} item - The item being dropped.
|
||||||
|
*/
|
||||||
|
async _onDropItem(item) {
|
||||||
|
const itemData = item.toObject()
|
||||||
|
await this.document.createEmbeddedDocuments("Item", [itemData], { renderSheet: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Actions
|
||||||
|
/**
|
||||||
|
* Handle toggling between Edit and Play mode.
|
||||||
|
* @param {Event} event - The initiating click event.
|
||||||
|
* @param {HTMLElement} target - The current target of the event listener.
|
||||||
|
*/
|
||||||
|
static #onToggleSheet(event, target) {
|
||||||
|
const modes = this.constructor.SHEET_MODES
|
||||||
|
this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle changing a Document's image.
|
||||||
|
* @param {PointerEvent} event - The originating click event.
|
||||||
|
* @param {HTMLElement} target - The capturing HTML element which defined a [data-action].
|
||||||
|
* @returns {Promise} The file picker promise.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditImage(event, target) {
|
||||||
|
const attr = target.dataset.edit
|
||||||
|
const current = foundry.utils.getProperty(this.document, attr)
|
||||||
|
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
|
||||||
|
const fp = new FilePicker({
|
||||||
|
current,
|
||||||
|
type: "image",
|
||||||
|
redirectToRoot: img ? [img] : [],
|
||||||
|
callback: path => {
|
||||||
|
this.document.update({ [attr]: path })
|
||||||
|
},
|
||||||
|
top: this.position.top + 40,
|
||||||
|
left: this.position.left + 10
|
||||||
|
})
|
||||||
|
return fp.browse()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Edit an existing item within the Actor.
|
||||||
|
* @param {PointerEvent} event - The originating click event.
|
||||||
|
* @param {HTMLElement} target - The capturing HTML element which defined a [data-action].
|
||||||
|
*/
|
||||||
|
static async #onItemEdit(event, target) {
|
||||||
|
const id = target.getAttribute("data-item-id")
|
||||||
|
const uuid = target.getAttribute("data-item-uuid")
|
||||||
|
let item = await fromUuid(uuid)
|
||||||
|
if (!item) item = this.document.items.get(id)
|
||||||
|
if (!item) return
|
||||||
|
item.sheet.render(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an existing item within the Actor.
|
||||||
|
* @param {PointerEvent} event - The originating click event.
|
||||||
|
* @param {HTMLElement} target - The capturing HTML element which defined a [data-action].
|
||||||
|
*/
|
||||||
|
static async #onItemDelete(event, target) {
|
||||||
|
const itemUuid = target.getAttribute("data-item-uuid")
|
||||||
|
const item = await fromUuid(itemUuid)
|
||||||
|
await item.deleteDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
const { HandlebarsApplicationMixin } = foundry.applications.api
|
||||||
|
|
||||||
|
export default class AwEItemSheet extends HandlebarsApplicationMixin(foundry.applications.sheets.ItemSheetV2) {
|
||||||
|
/**
|
||||||
|
* Different sheet modes.
|
||||||
|
* @enum {number}
|
||||||
|
*/
|
||||||
|
static SHEET_MODES = { EDIT: 0, PLAY: 1 }
|
||||||
|
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options)
|
||||||
|
this.#dragDrop = this.#createDragDropHandlers()
|
||||||
|
}
|
||||||
|
|
||||||
|
#dragDrop
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["awemmy", "item"],
|
||||||
|
position: {
|
||||||
|
width: 600,
|
||||||
|
height: "auto"
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
submitOnChange: true
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
resizable: true
|
||||||
|
},
|
||||||
|
dragDrop: [{ dragSelector: "[data-drag]", dropSelector: null }],
|
||||||
|
actions: {
|
||||||
|
toggleSheet: AwEItemSheet.#onToggleSheet,
|
||||||
|
editImage: AwEItemSheet.#onEditImage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current sheet mode.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
_sheetMode = this.constructor.SHEET_MODES.PLAY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the sheet currently in 'Play' mode?
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isPlayMode() {
|
||||||
|
return this._sheetMode === this.constructor.SHEET_MODES.PLAY
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the sheet currently in 'Edit' mode?
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isEditMode() {
|
||||||
|
return this._sheetMode === this.constructor.SHEET_MODES.EDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext()
|
||||||
|
context.fields = this.document.schema.fields
|
||||||
|
context.systemFields = this.document.system.schema.fields
|
||||||
|
context.item = this.document
|
||||||
|
context.system = this.document.system
|
||||||
|
context.source = this.document.toObject()
|
||||||
|
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
this.document.system.description, { async: true }
|
||||||
|
)
|
||||||
|
context.isEditMode = this.isEditMode
|
||||||
|
context.isPlayMode = this.isPlayMode
|
||||||
|
context.isEditable = this.isEditable
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_onRender(context, options) {
|
||||||
|
super._onRender(context, options)
|
||||||
|
this.#dragDrop.forEach(d => d.bind(this.element))
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Drag-and-Drop Workflow
|
||||||
|
/**
|
||||||
|
* Create drag-and-drop workflow handlers for this Application.
|
||||||
|
* @returns {DragDrop[]} An array of DragDrop handlers.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
#createDragDropHandlers() {
|
||||||
|
return this.options.dragDrop.map(d => {
|
||||||
|
d.permissions = {
|
||||||
|
dragstart: this._canDragStart.bind(this),
|
||||||
|
drop: this._canDragDrop.bind(this)
|
||||||
|
}
|
||||||
|
d.callbacks = {
|
||||||
|
dragstart: this._onDragStart.bind(this),
|
||||||
|
dragover: this._onDragOver.bind(this),
|
||||||
|
drop: this._onDrop.bind(this)
|
||||||
|
}
|
||||||
|
return new foundry.applications.ux.DragDrop.implementation(d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define whether a user is able to begin a dragstart workflow for a given drag selector.
|
||||||
|
* @param {string} selector - The candidate HTML selector for dragging.
|
||||||
|
* @returns {boolean} Can the current user drag this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragStart(selector) {
|
||||||
|
return this.isEditable
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define whether a user is able to conclude a drag-and-drop workflow for a given drop selector.
|
||||||
|
* @param {string} selector - The candidate HTML selector for the drop target.
|
||||||
|
* @returns {boolean} Can the current user drop on this selector?
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_canDragDrop(selector) {
|
||||||
|
return this.isEditable && this.document.isOwner
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur at the beginning of a drag start workflow.
|
||||||
|
* @param {DragEvent} event - The originating DragEvent.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDragStart(event) {
|
||||||
|
if ("link" in event.target.dataset) return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur when a dragged element is over a drop target.
|
||||||
|
* @param {DragEvent} event - The originating DragEvent.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_onDragOver(event) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions which occur when a dragged element is dropped on a target.
|
||||||
|
* @param {DragEvent} event - The originating DragEvent.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
async _onDrop(event) {}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Actions
|
||||||
|
/**
|
||||||
|
* Handle toggling between Edit and Play mode.
|
||||||
|
* @param {Event} event - The initiating click event.
|
||||||
|
* @param {HTMLElement} target - The current target of the event listener.
|
||||||
|
*/
|
||||||
|
static #onToggleSheet(event, target) {
|
||||||
|
const modes = this.constructor.SHEET_MODES
|
||||||
|
this._sheetMode = this.isEditMode ? modes.PLAY : modes.EDIT
|
||||||
|
this.render()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle changing a Document's image.
|
||||||
|
* @param {PointerEvent} event - The originating click event.
|
||||||
|
* @param {HTMLElement} target - The capturing HTML element which defined a [data-action].
|
||||||
|
* @returns {Promise} The file picker promise.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
static async #onEditImage(event, target) {
|
||||||
|
const attr = target.dataset.edit
|
||||||
|
const current = foundry.utils.getProperty(this.document, attr)
|
||||||
|
const { img } = this.document.constructor.getDefaultArtwork?.(this.document.toObject()) ?? {}
|
||||||
|
const fp = new FilePicker({
|
||||||
|
current,
|
||||||
|
type: "image",
|
||||||
|
redirectToRoot: img ? [img] : [],
|
||||||
|
callback: path => {
|
||||||
|
this.document.update({ [attr]: path })
|
||||||
|
},
|
||||||
|
top: this.position.top + 40,
|
||||||
|
left: this.position.left + 10
|
||||||
|
})
|
||||||
|
return fp.browse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// #endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import AwEActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwECharacterSheet extends AwEActorSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["character"],
|
||||||
|
position: {
|
||||||
|
width: 960,
|
||||||
|
height: 780
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
contentClasses: ["character-content"]
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
createAbility: AwECharacterSheet.#onCreateAbility,
|
||||||
|
createWeapon: AwECharacterSheet.#onCreateWeapon,
|
||||||
|
createKit: AwECharacterSheet.#onCreateKit,
|
||||||
|
createEquipment: AwECharacterSheet.#onCreateEquipment,
|
||||||
|
flowPointsPlus: AwECharacterSheet.#onFlowPointsPlus,
|
||||||
|
flowPointsMinus: AwECharacterSheet.#onFlowPointsMinus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/character-main.hbs"
|
||||||
|
},
|
||||||
|
tabs: {
|
||||||
|
template: "templates/generic/tab-navigation.hbs"
|
||||||
|
},
|
||||||
|
biography: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/character-biography.hbs"
|
||||||
|
},
|
||||||
|
equipment: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/character-equipment.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
tabGroups = {
|
||||||
|
sheet: "main"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare an array of form header tabs.
|
||||||
|
* @returns {Record<string, Partial<ApplicationTab>>} The tab objects.
|
||||||
|
*/
|
||||||
|
#getTabs() {
|
||||||
|
const tabs = {
|
||||||
|
main: { id: "main", group: "sheet", icon: "fa-solid fa-user", label: "AWEMMY.Sheet.Tab.Main" },
|
||||||
|
biography: { id: "biography", group: "sheet", icon: "fa-solid fa-book", label: "AWEMMY.Sheet.Tab.Biography" },
|
||||||
|
equipment: { id: "equipment", group: "sheet", icon: "fa-solid fa-backpack", label: "AWEMMY.Sheet.Tab.Equipment" }
|
||||||
|
}
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] === v.id
|
||||||
|
v.cssClass = v.active ? "active" : ""
|
||||||
|
}
|
||||||
|
return tabs
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext()
|
||||||
|
context.tabs = this.#getTabs()
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _preparePartContext(partId, context) {
|
||||||
|
const doc = this.document
|
||||||
|
switch (partId) {
|
||||||
|
case "main":
|
||||||
|
context.tab = context.tabs.main
|
||||||
|
context.abilities = doc.itemTypes.ability
|
||||||
|
break
|
||||||
|
case "biography":
|
||||||
|
context.tab = context.tabs.biography
|
||||||
|
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
doc.system.description, { async: true }
|
||||||
|
)
|
||||||
|
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
doc.system.notes, { async: true }
|
||||||
|
)
|
||||||
|
break
|
||||||
|
case "equipment":
|
||||||
|
context.tab = context.tabs.equipment
|
||||||
|
context.kits = doc.itemTypes.kit
|
||||||
|
context.weapons = doc.itemTypes.weapon
|
||||||
|
context.equipments = doc.itemTypes.equipment
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _onDrop(event) {
|
||||||
|
if (!this.isEditable || !this.isEditMode) return
|
||||||
|
const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event)
|
||||||
|
if (data.type === "Item") {
|
||||||
|
const item = await fromUuid(data.uuid)
|
||||||
|
return this._onDropItem(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ability item.
|
||||||
|
* @param {Event} event - The triggering event.
|
||||||
|
* @param {HTMLElement} target - The target element.
|
||||||
|
*/
|
||||||
|
static #onCreateAbility(event, target) {
|
||||||
|
this.document.createEmbeddedDocuments("Item", [{ name: "New Ability", type: "ability" }])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new weapon item.
|
||||||
|
* @param {Event} event - The triggering event.
|
||||||
|
* @param {HTMLElement} target - The target element.
|
||||||
|
*/
|
||||||
|
static #onCreateWeapon(event, target) {
|
||||||
|
this.document.createEmbeddedDocuments("Item", [{ name: "New Weapon", type: "weapon" }])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new kit item.
|
||||||
|
* @param {Event} event - The triggering event.
|
||||||
|
* @param {HTMLElement} target - The target element.
|
||||||
|
*/
|
||||||
|
static #onCreateKit(event, target) {
|
||||||
|
this.document.createEmbeddedDocuments("Item", [{ name: "New Kit", type: "kit" }])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new equipment item.
|
||||||
|
* @param {Event} event - The triggering event.
|
||||||
|
* @param {HTMLElement} target - The target element.
|
||||||
|
*/
|
||||||
|
static #onCreateEquipment(event, target) {
|
||||||
|
this.document.createEmbeddedDocuments("Item", [{ name: "New Equipment", type: "equipment" }])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase flow points by 1.
|
||||||
|
* @param {Event} event - The triggering event.
|
||||||
|
* @param {HTMLElement} target - The target element.
|
||||||
|
*/
|
||||||
|
static #onFlowPointsPlus(event, target) {
|
||||||
|
const current = this.actor.system.flowPoints.value
|
||||||
|
this.actor.update({ "system.flowPoints.value": current + 1 })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrease flow points by 1.
|
||||||
|
* @param {Event} event - The triggering event.
|
||||||
|
* @param {HTMLElement} target - The target element.
|
||||||
|
*/
|
||||||
|
static #onFlowPointsMinus(event, target) {
|
||||||
|
const current = this.actor.system.flowPoints.value
|
||||||
|
this.actor.update({ "system.flowPoints.value": Math.max(0, current - 1) })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import AwEActorSheet from "./base-actor-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwECreatureSheet extends AwEActorSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["creature"],
|
||||||
|
position: {
|
||||||
|
width: 700,
|
||||||
|
height: "auto"
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
contentClasses: ["creature-content"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/creature-main.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
async _prepareContext() {
|
||||||
|
const context = await super._prepareContext()
|
||||||
|
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
|
||||||
|
this.document.system.description, { async: true }
|
||||||
|
)
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import AwEItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwEEquipmentSheet extends AwEItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["equipment"],
|
||||||
|
position: { width: 620 },
|
||||||
|
window: { contentClasses: ["equipment-content"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/equipment.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import AwEItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwEFieldSheet extends AwEItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["field"],
|
||||||
|
position: { width: 620 },
|
||||||
|
window: { contentClasses: ["field-content"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/field.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import AwEItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwEKitSheet extends AwEItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["kit"],
|
||||||
|
position: { width: 620 },
|
||||||
|
window: { contentClasses: ["kit-content"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/kit.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import AwEItemSheet from "./base-item-sheet.mjs"
|
||||||
|
|
||||||
|
export default class AwEWeaponSheet extends AwEItemSheet {
|
||||||
|
/** @override */
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
classes: ["weapon"],
|
||||||
|
position: { width: 620 },
|
||||||
|
window: { contentClasses: ["weapon-content"] }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/fvtt-adventures-with-emmy/templates/weapon.hbs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
export const SYSTEM_ID = "fvtt-adventures-with-emmy"
|
||||||
|
export const DEV_MODE = false
|
||||||
|
|
||||||
|
export const ATTRIBUTES = {
|
||||||
|
agility: { id: "agility", abbrev: "AGI", label: "AWEMMY.Attribute.Agility" },
|
||||||
|
fitness: { id: "fitness", abbrev: "FIT", label: "AWEMMY.Attribute.Fitness" },
|
||||||
|
awareness: { id: "awareness", abbrev: "AWA", label: "AWEMMY.Attribute.Awareness" },
|
||||||
|
influence: { id: "influence", abbrev: "INF", label: "AWEMMY.Attribute.Influence" }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CONDITIONS = {
|
||||||
|
edge: { id: "edge", label: "AWEMMY.Condition.Edge" },
|
||||||
|
hampered: { id: "hampered", label: "AWEMMY.Condition.Hampered" },
|
||||||
|
inhibited: { id: "inhibited", label: "AWEMMY.Condition.Inhibited" },
|
||||||
|
jumbled: { id: "jumbled", label: "AWEMMY.Condition.Jumbled" },
|
||||||
|
mishap: { id: "mishap", label: "AWEMMY.Condition.Mishap" },
|
||||||
|
prone: { id: "prone", label: "AWEMMY.Condition.Prone" },
|
||||||
|
quickened: { id: "quickened", label: "AWEMMY.Condition.Quickened" },
|
||||||
|
slowed: { id: "slowed", label: "AWEMMY.Condition.Slowed" },
|
||||||
|
vulnerable: { id: "vulnerable", label: "AWEMMY.Condition.Vulnerable" }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ABILITY_COST = {
|
||||||
|
"one": { id: "one", label: "Δ" },
|
||||||
|
"two": { id: "two", label: "ΔΔ" },
|
||||||
|
"three": { id: "three", label: "ΔΔΔ" },
|
||||||
|
"reaction": { id: "reaction", label: "↩" },
|
||||||
|
"free": { id: "free", label: "AWEMMY.Ability.Cost.Free" },
|
||||||
|
"none": { id: "none", label: "—" }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ABILITY_TYPE = {
|
||||||
|
"field": { id: "field", label: "AWEMMY.Ability.Type.Field" },
|
||||||
|
"archetype": { id: "archetype", label: "AWEMMY.Ability.Type.Archetype" },
|
||||||
|
"general": { id: "general", label: "AWEMMY.Ability.Type.General" },
|
||||||
|
"beginner": { id: "beginner", label: "AWEMMY.Ability.Type.Beginner" }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OUTCOME_LABELS = {
|
||||||
|
criticalSuccess: "AWEMMY.Roll.CriticalSuccess",
|
||||||
|
success: "AWEMMY.Roll.Success",
|
||||||
|
failure: "AWEMMY.Roll.Failure",
|
||||||
|
criticalFailure: "AWEMMY.Roll.CriticalFailure"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ASCII = `
|
||||||
|
_ _ _ _ _ _ _____
|
||||||
|
/ \\ __| |_ _____ _ __ | |_ _ _ _ __ ___ ___| | | | | | ____|_ __ ___ _ __ ___ _ _
|
||||||
|
/ _ \\ / _\` \\ \\ / / _ \\ '_ \\| __| | | | '__/ _ \\/ __| | | | | | _| | '_ \` _ \\| '_ \` _ \\| | | |
|
||||||
|
/ ___ \\ (_| |\\ V / __/ | | | |_| |_| | | | __/\\__ \\ |_| | |___| |___| | | | | | | | | | | |_| |
|
||||||
|
/_/ \\_\\__,_| \\_/ \\___|_| |_|\\__|\\__,_|_| \\___||___/\\___/|_____|_____|_| |_| |_|_| |_| |_|\\__, |
|
||||||
|
|___/
|
||||||
|
`
|
||||||
|
|
||||||
|
// Re-export all for convenience
|
||||||
|
export const SYSTEM = {
|
||||||
|
SYSTEM_ID,
|
||||||
|
DEV_MODE,
|
||||||
|
ATTRIBUTES,
|
||||||
|
CONDITIONS,
|
||||||
|
ABILITY_COST,
|
||||||
|
ABILITY_TYPE,
|
||||||
|
OUTCOME_LABELS,
|
||||||
|
ASCII
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export { default as AwEActor } from "./actor.mjs"
|
||||||
|
export { default as AwEItem } from "./item.mjs"
|
||||||
|
export { default as AwERoll } from "./roll.mjs"
|
||||||
|
export { default as AwEChatMessage } from "./chat-message.mjs"
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
export default class AwEActor extends Actor {
|
||||||
|
/** @override */
|
||||||
|
prepareData() {
|
||||||
|
super.prepareData()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
prepareBaseData() {}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
prepareDerivedData() {
|
||||||
|
const actorData = this
|
||||||
|
this._prepareCharacterData(actorData)
|
||||||
|
this._prepareCreatureData(actorData)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare character data.
|
||||||
|
* @param {object} actorData - The actor data.
|
||||||
|
*/
|
||||||
|
_prepareCharacterData(actorData) {
|
||||||
|
if (actorData.type !== "character") return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare creature data.
|
||||||
|
* @param {object} actorData - The actor data.
|
||||||
|
*/
|
||||||
|
_prepareCreatureData(actorData) {
|
||||||
|
if (actorData.type !== "creature") return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roll an attribute check.
|
||||||
|
* @param {string} attributeId - The attribute to roll.
|
||||||
|
* @param {object} options - Roll options.
|
||||||
|
* @returns {Promise<Roll>} The roll result.
|
||||||
|
*/
|
||||||
|
async rollAttribute(attributeId, options = {}) {
|
||||||
|
const attribute = this.system.attributes[attributeId]
|
||||||
|
if (!attribute) return null
|
||||||
|
|
||||||
|
const mod = attribute.mod ?? 0
|
||||||
|
const formula = `1d20 + ${mod}`
|
||||||
|
const roll = new Roll(formula)
|
||||||
|
await roll.evaluate()
|
||||||
|
|
||||||
|
// Determine outcome vs DC if provided
|
||||||
|
let outcome = null
|
||||||
|
if (options.dc !== undefined) {
|
||||||
|
const total = roll.total
|
||||||
|
const dc = options.dc
|
||||||
|
if (total >= dc + 10) outcome = "criticalSuccess"
|
||||||
|
else if (total >= dc) outcome = "success"
|
||||||
|
else if (total <= dc - 10) outcome = "criticalFailure"
|
||||||
|
else outcome = "failure"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send to chat
|
||||||
|
const attrLabel = attributeId.charAt(0).toUpperCase() + attributeId.slice(1)
|
||||||
|
const flavor = options.flavor || game.i18n.localize(`AWEMMY.Attribute.${attrLabel}`)
|
||||||
|
await roll.toMessage({
|
||||||
|
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||||
|
flavor,
|
||||||
|
rollMode: game.settings.get("core", "rollMode")
|
||||||
|
})
|
||||||
|
|
||||||
|
return { roll, outcome }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export default class AwEChatMessage extends ChatMessage {
|
||||||
|
/** @override */
|
||||||
|
async getHTML(...args) {
|
||||||
|
const html = await super.getHTML(...args)
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export default class AwEItem extends Item {
|
||||||
|
/** @override */
|
||||||
|
prepareData() {
|
||||||
|
super.prepareData()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export default class AwERoll extends Roll {
|
||||||
|
constructor(formula, data = {}, options = {}) {
|
||||||
|
super(formula, data, options)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export { default as AwECharacter } from "./character.mjs"
|
||||||
|
export { default as AwECreature } from "./creature.mjs"
|
||||||
|
export { default as AwEAbility } from "./ability.mjs"
|
||||||
|
export { default as AwEField } from "./field.mjs"
|
||||||
|
export { default as AwEArchetype } from "./archetype.mjs"
|
||||||
|
export { default as AwEBackground } from "./background.mjs"
|
||||||
|
export { default as AwEKit } from "./kit.mjs"
|
||||||
|
export { default as AwEWeapon } from "./weapon.mjs"
|
||||||
|
export { default as AwEEquipment } from "./equipment.mjs"
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
|
export default class AwEAbility extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.abilityType = new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
nullable: false,
|
||||||
|
initial: "field",
|
||||||
|
choices: Object.keys(SYSTEM.ABILITY_TYPE)
|
||||||
|
})
|
||||||
|
schema.cost = new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
nullable: false,
|
||||||
|
initial: "one",
|
||||||
|
choices: Object.keys(SYSTEM.ABILITY_COST)
|
||||||
|
})
|
||||||
|
schema.frequency = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.requirements = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.trigger = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.traits = new fields.ArrayField(new fields.StringField())
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export default class AwEArchetype extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.prerequisiteLevel = new fields.NumberField({
|
||||||
|
required: true, nullable: false, integer: true, initial: 2, min: 1
|
||||||
|
})
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export default class AwEBackground extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.bonus = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
|
export default class AwECharacter extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.notes = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
|
||||||
|
// Identity
|
||||||
|
schema.pronouns = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.fieldName = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.specialization = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.archetypeName = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.backgroundName = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
|
||||||
|
// Core stats
|
||||||
|
schema.level = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1, max: 10 })
|
||||||
|
schema.stride = new fields.NumberField({ ...requiredInteger, initial: 5, min: 0 })
|
||||||
|
|
||||||
|
// Hit Points
|
||||||
|
schema.hp = new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ ...requiredInteger, initial: 10, min: 0 }),
|
||||||
|
max: new fields.NumberField({ ...requiredInteger, initial: 10, min: 0 }),
|
||||||
|
temp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Flow Points
|
||||||
|
schema.flowPoints = new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
|
||||||
|
temp: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Attributes: agility, fitness, awareness, influence
|
||||||
|
// boostLevel: how many boosts applied (0-4)
|
||||||
|
// mod = level + boostLevel (computed in prepareDerivedData)
|
||||||
|
// dc = 10 + mod (computed)
|
||||||
|
// bonus: manual +/- bonus
|
||||||
|
const attributeField = () => new fields.SchemaField({
|
||||||
|
boostLevel: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0, max: 4 }),
|
||||||
|
bonus: new fields.NumberField({ required: true, nullable: false, integer: true, initial: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
schema.attributes = new fields.SchemaField(
|
||||||
|
Object.values(SYSTEM.ATTRIBUTES).reduce((obj, attr) => {
|
||||||
|
obj[attr.id] = attributeField()
|
||||||
|
return obj
|
||||||
|
}, {})
|
||||||
|
)
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
prepareDerivedData() {
|
||||||
|
super.prepareDerivedData()
|
||||||
|
const level = this.level
|
||||||
|
|
||||||
|
for (const attrId of Object.keys(SYSTEM.ATTRIBUTES)) {
|
||||||
|
const attr = this.attributes[attrId]
|
||||||
|
attr.mod = level + attr.boostLevel + attr.bonus
|
||||||
|
attr.dc = 10 + attr.mod
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute max HP if not overridden
|
||||||
|
// Base HP = 10 + (level * 2) + fitness modifier
|
||||||
|
const fitnessMod = this.attributes.fitness.mod ?? 0
|
||||||
|
if (this.hp.max === 10) {
|
||||||
|
this.hp.max = 10 + (level * 2) + fitnessMod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
export default class AwECreature extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
|
||||||
|
schema.level = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 })
|
||||||
|
schema.stride = new fields.NumberField({ ...requiredInteger, initial: 5, min: 0 })
|
||||||
|
|
||||||
|
schema.hp = new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ ...requiredInteger, initial: 10, min: 0 }),
|
||||||
|
max: new fields.NumberField({ ...requiredInteger, initial: 10, min: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Raw attribute values (mod = value directly, no boosts)
|
||||||
|
schema.attributes = new fields.SchemaField({
|
||||||
|
agility: new fields.SchemaField({
|
||||||
|
mod: new fields.NumberField({ ...requiredInteger, initial: 2 }),
|
||||||
|
dc: new fields.NumberField({ ...requiredInteger, initial: 12 })
|
||||||
|
}),
|
||||||
|
fitness: new fields.SchemaField({
|
||||||
|
mod: new fields.NumberField({ ...requiredInteger, initial: 2 }),
|
||||||
|
dc: new fields.NumberField({ ...requiredInteger, initial: 12 })
|
||||||
|
}),
|
||||||
|
awareness: new fields.SchemaField({
|
||||||
|
mod: new fields.NumberField({ ...requiredInteger, initial: 2 }),
|
||||||
|
dc: new fields.NumberField({ ...requiredInteger, initial: 12 })
|
||||||
|
}),
|
||||||
|
influence: new fields.SchemaField({
|
||||||
|
mod: new fields.NumberField({ ...requiredInteger, initial: 2 }),
|
||||||
|
dc: new fields.NumberField({ ...requiredInteger, initial: 12 })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Eureka Rubric
|
||||||
|
schema.eurekaClaims = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.eurekaEvidence = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.eurekaThreshold1 = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.eurekaThreshold2 = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.eurekaHints = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export default class AwEEquipment extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.quantity = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
|
||||||
|
schema.weight = new fields.NumberField({ required: true, nullable: false, initial: 0, min: 0 })
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
|
export default class AwEField extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.keyAttribute = new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
nullable: false,
|
||||||
|
initial: "agility",
|
||||||
|
choices: Object.keys(SYSTEM.ATTRIBUTES)
|
||||||
|
})
|
||||||
|
schema.keyAttribute2 = new fields.StringField({
|
||||||
|
required: false,
|
||||||
|
nullable: true,
|
||||||
|
initial: null,
|
||||||
|
choices: [...Object.keys(SYSTEM.ATTRIBUTES), null]
|
||||||
|
})
|
||||||
|
schema.specializations = new fields.ArrayField(new fields.StringField())
|
||||||
|
schema.knowledgeBonus = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export default class AwEKit extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.fieldName = new fields.StringField({ initial: "", required: false, nullable: true })
|
||||||
|
schema.charges = new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
|
||||||
|
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
|
||||||
|
})
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { SYSTEM } from "../config/system.mjs"
|
||||||
|
|
||||||
|
export default class AwEWeapon extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields
|
||||||
|
const requiredInteger = { required: true, nullable: false, integer: true }
|
||||||
|
const schema = {}
|
||||||
|
|
||||||
|
schema.description = new fields.HTMLField({ required: true, textSearch: true })
|
||||||
|
schema.range = new fields.NumberField({ ...requiredInteger, initial: 1, min: 0 })
|
||||||
|
schema.damageFormula = new fields.StringField({ initial: "1d6", required: true, nullable: false })
|
||||||
|
schema.damageType = new fields.StringField({ initial: "physical", required: true, nullable: false })
|
||||||
|
schema.attackAttribute = new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
nullable: false,
|
||||||
|
initial: "agility",
|
||||||
|
choices: Object.keys(SYSTEM.ATTRIBUTES)
|
||||||
|
})
|
||||||
|
schema.traits = new fields.ArrayField(new fields.StringField())
|
||||||
|
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+3996
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "fvtt-adventures-with-emmy",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.8.0",
|
||||||
|
"@foundryvtt/foundryvtt-cli": "^1.0.2",
|
||||||
|
"commander": "^11.1.0",
|
||||||
|
"eslint": "^9.9.0",
|
||||||
|
"eslint-config-prettier": "^9.1.0",
|
||||||
|
"eslint-plugin-jsdoc": "^48.11.0",
|
||||||
|
"eslint-plugin-prettier": "^5.2.1",
|
||||||
|
"globals": "^15.9.0",
|
||||||
|
"less": "^4.1.3",
|
||||||
|
"prettier": "^3.3.3"
|
||||||
|
},
|
||||||
|
"author": "LeRatierBretonnien",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"gulp": "^5.0.0",
|
||||||
|
"gulp-less": "^5.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"packPacks": "node ./tools/packPacks.mjs",
|
||||||
|
"unpackPacks": "node ./tools/unpackPacks.mjs"
|
||||||
|
},
|
||||||
|
"description": "Adventures with Emmy - STEM Education RPG for Foundry Virtual TableTop",
|
||||||
|
"main": "gulpfile.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://www.uberwald.me/gitea/uberwald/fvtt-adventures-with-emmy.git"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,455 @@
|
|||||||
|
// Adventures with Emmy - Main LESS stylesheet
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Variables
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
@color-primary: #3a5a8c;
|
||||||
|
@color-secondary: #e8a020;
|
||||||
|
@color-accent: #2ecc71;
|
||||||
|
@color-bg: #f5f0e8;
|
||||||
|
@color-text: #2c2c2c;
|
||||||
|
@color-text-light: #6c6c6c;
|
||||||
|
@color-border: #c8b89a;
|
||||||
|
@color-success: #27ae60;
|
||||||
|
@color-failure: #e74c3c;
|
||||||
|
@color-critical-success: #1abc9c;
|
||||||
|
@color-critical-failure: #8e44ad;
|
||||||
|
|
||||||
|
@font-heading: "Signika", "Palatino Linotype", serif;
|
||||||
|
@font-body: "Signika", "Book Antiqua", serif;
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Base Styles
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
.awemmy {
|
||||||
|
font-family: @font-body;
|
||||||
|
color: @color-text;
|
||||||
|
background: @color-bg;
|
||||||
|
|
||||||
|
.window-header {
|
||||||
|
background: @color-primary;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
.window-title {
|
||||||
|
font-family: @font-heading;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Sheet Controls
|
||||||
|
// --------------------------------------------------------
|
||||||
|
.sheet-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.toggle-sheet {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border: 1px solid @color-border;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: @color-secondary;
|
||||||
|
color: white;
|
||||||
|
border-color: @color-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Actor Header
|
||||||
|
// --------------------------------------------------------
|
||||||
|
.actor-header {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: lighten(@color-primary, 50%);
|
||||||
|
border-bottom: 2px solid @color-border;
|
||||||
|
|
||||||
|
.actor-img {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 2px solid @color-border;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: @color-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.actor-name {
|
||||||
|
font-family: @font-heading;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Attributes Table
|
||||||
|
// --------------------------------------------------------
|
||||||
|
.attributes-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
|
||||||
|
thead tr {
|
||||||
|
background: @color-primary;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 0.4rem 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr {
|
||||||
|
&:nth-child(even) {
|
||||||
|
background: lighten(@color-primary, 55%);
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 0.35rem 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 1px solid @color-border;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.attr-label {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rollable {
|
||||||
|
cursor: pointer;
|
||||||
|
color: @color-primary;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @color-secondary;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// HP / Flow Points
|
||||||
|
// --------------------------------------------------------
|
||||||
|
.resource-block {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: @color-text-light;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-values {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
|
||||||
|
input {
|
||||||
|
width: 45px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
color: @color-text-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-stepper {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.15rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid @color-border;
|
||||||
|
border-radius: 3px;
|
||||||
|
background: white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: @color-secondary;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Item Lists
|
||||||
|
// --------------------------------------------------------
|
||||||
|
.item-list {
|
||||||
|
.item-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.3rem 0.25rem;
|
||||||
|
border-bottom: 1px solid lighten(@color-border, 10%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: lighten(@color-primary, 55%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-img {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid @color-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-name {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: @color-text-light;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-action="delete"]:hover {
|
||||||
|
color: @color-failure;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-add {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px dashed @color-border;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0.25rem;
|
||||||
|
color: @color-text-light;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @color-primary;
|
||||||
|
border-color: @color-primary;
|
||||||
|
background: lighten(@color-primary, 55%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Fieldsets
|
||||||
|
// --------------------------------------------------------
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid @color-border;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
|
||||||
|
legend {
|
||||||
|
font-family: @font-heading;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-primary;
|
||||||
|
padding: 0 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Tab Navigation
|
||||||
|
// --------------------------------------------------------
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 0;
|
||||||
|
border-bottom: 2px solid @color-primary;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
padding: 0.4rem 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-bottom: none;
|
||||||
|
color: @color-text-light;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: @color-primary;
|
||||||
|
background: lighten(@color-primary, 55%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: white;
|
||||||
|
color: @color-primary;
|
||||||
|
border-color: @color-primary;
|
||||||
|
border-bottom-color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Creature Sheet - Eureka Rubric
|
||||||
|
// --------------------------------------------------------
|
||||||
|
.eureka-rubric {
|
||||||
|
background: lighten(@color-accent, 45%);
|
||||||
|
border: 1px solid @color-accent;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
|
.eureka-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: bold;
|
||||||
|
min-width: 80px;
|
||||||
|
color: darken(@color-accent, 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
input, textarea {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Item Sheets
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
.awemmy.item {
|
||||||
|
.item-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: lighten(@color-primary, 50%);
|
||||||
|
border-bottom: 2px solid @color-border;
|
||||||
|
|
||||||
|
.item-img {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
object-fit: cover;
|
||||||
|
border: 2px solid @color-border;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: @color-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
font-family: @font-heading;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid @color-border;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-body {
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
|
||||||
|
label {
|
||||||
|
min-width: 120px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: @color-text-light;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Chat Message
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
.awemmy-chat {
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
|
.chat-roll-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
|
||||||
|
.chat-actor-img {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid @color-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-roll-label {
|
||||||
|
font-family: @font-heading;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: @color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.roll-result {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
background: lighten(@color-primary, 50%);
|
||||||
|
color: @color-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outcome-badge {
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.3rem 0.75rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
|
||||||
|
&.critical-success { background: @color-critical-success; color: white; }
|
||||||
|
&.success { background: @color-success; color: white; }
|
||||||
|
&.failure { background: @color-failure; color: white; }
|
||||||
|
&.critical-failure { background: @color-critical-failure; color: white; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"id": "fvtt-adventures-with-emmy",
|
||||||
|
"title": "Adventures with Emmy",
|
||||||
|
"description": "Adventures with Emmy - STEM Education RPG",
|
||||||
|
"manifest": "#{MANIFEST}#",
|
||||||
|
"download": "#{DOWNLOAD}#",
|
||||||
|
"url": "#{URL}#",
|
||||||
|
"license": "LICENSE",
|
||||||
|
"version": "13.0.0",
|
||||||
|
"authors": [{ "name": "Uberwald", "discord": "LeRatierBretonnien" }],
|
||||||
|
"compatibility": { "minimum": "13", "verified": "13" },
|
||||||
|
"esmodules": ["adventures-with-emmy.mjs"],
|
||||||
|
"styles": ["css/adventures-with-emmy.css"],
|
||||||
|
"languages": [{ "lang": "en", "name": "English", "path": "lang/en.json" }],
|
||||||
|
"documentTypes": {
|
||||||
|
"Actor": {
|
||||||
|
"character": { "htmlFields": ["description", "notes"] },
|
||||||
|
"creature": { "htmlFields": ["description"] }
|
||||||
|
},
|
||||||
|
"Item": {
|
||||||
|
"ability": { "htmlFields": ["description"] },
|
||||||
|
"field": { "htmlFields": ["description"] },
|
||||||
|
"archetype": { "htmlFields": ["description"] },
|
||||||
|
"background": { "htmlFields": ["description"] },
|
||||||
|
"kit": { "htmlFields": ["description"] },
|
||||||
|
"weapon": { "htmlFields": ["description"] },
|
||||||
|
"equipment": { "htmlFields": ["description"] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"grid": { "distance": 1, "units": "m" },
|
||||||
|
"primaryTokenAttribute": "hp",
|
||||||
|
"flags": {
|
||||||
|
"hotReload": {
|
||||||
|
"extensions": ["css", "hbs", "json"],
|
||||||
|
"paths": ["css/", "lang/", "assets/", "templates/"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<section>
|
||||||
|
<div class="item-header">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
||||||
|
{{formInput fields.name value=source.name}}
|
||||||
|
</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Type</label>
|
||||||
|
{{formField systemFields.abilityType value=system.abilityType localize=true}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Ability.Cost"}}</label>
|
||||||
|
{{formField systemFields.cost value=system.cost}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Ability.Frequency"}}</label>
|
||||||
|
{{formInput systemFields.frequency value=system.frequency}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Ability.Requirements"}}</label>
|
||||||
|
{{formInput systemFields.requirements value=system.requirements}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Ability.Trigger"}}</label>
|
||||||
|
{{formInput systemFields.trigger value=system.trigger}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<section>
|
||||||
|
<div class="item-header">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
||||||
|
{{formInput fields.name value=source.name}}
|
||||||
|
</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Prerequisite Level</label>
|
||||||
|
{{formInput systemFields.prerequisiteLevel value=system.prerequisiteLevel}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<section>
|
||||||
|
<div class="item-header">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
||||||
|
{{formInput fields.name value=source.name}}
|
||||||
|
</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Background.Bonus"}}</label>
|
||||||
|
{{formInput systemFields.bonus value=system.bonus}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<section class="tab character-biography {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Identity</legend>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Pronouns</label>
|
||||||
|
{{formInput systemFields.pronouns value=system.pronouns disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Kit.Field"}}</label>
|
||||||
|
{{formInput systemFields.fieldName value=system.fieldName disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Specialization</label>
|
||||||
|
{{formInput systemFields.specialization value=system.specialization disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Item.Archetype"}}</label>
|
||||||
|
{{formInput systemFields.archetypeName value=system.archetypeName disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Item.Background"}}</label>
|
||||||
|
{{formInput systemFields.backgroundName value=system.backgroundName disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<legend>Notes</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.notes
|
||||||
|
enriched=enrichedNotes
|
||||||
|
value=system.notes
|
||||||
|
name="system.notes"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<section class="tab character-equipment {{tab.cssClass}}" data-tab="{{tab.id}}" data-group="{{tab.group}}">
|
||||||
|
|
||||||
|
{{!-- Kits --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "AWEMMY.Item.Kit"}}</legend>
|
||||||
|
<div class="item-list">
|
||||||
|
{{#each kits as |item|}}
|
||||||
|
<div class="item-row" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
|
||||||
|
<div class="item-name">{{item.name}}</div>
|
||||||
|
<div class="item-charges">{{item.system.charges.value}}/{{item.system.charges.max}}</div>
|
||||||
|
<div class="item-controls">
|
||||||
|
<a data-action="edit" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
|
||||||
|
{{#if ../isEditMode}}
|
||||||
|
<a data-action="delete" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{#if isEditMode}}
|
||||||
|
<div class="item-add">
|
||||||
|
<button type="button" data-action="createKit"><i class="fas fa-plus"></i> {{localize "AWEMMY.Item.Kit"}}</button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{{!-- Weapons --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "AWEMMY.Item.Weapon"}}</legend>
|
||||||
|
<div class="item-list">
|
||||||
|
{{#each weapons as |item|}}
|
||||||
|
<div class="item-row" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
|
||||||
|
<div class="item-name">{{item.name}}</div>
|
||||||
|
<div class="item-damage">{{item.system.damageFormula}} ({{item.system.damageType}})</div>
|
||||||
|
<div class="item-range">Range: {{item.system.range}}</div>
|
||||||
|
<div class="item-controls">
|
||||||
|
<a data-action="edit" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
|
||||||
|
{{#if ../isEditMode}}
|
||||||
|
<a data-action="delete" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{#if isEditMode}}
|
||||||
|
<div class="item-add">
|
||||||
|
<button type="button" data-action="createWeapon"><i class="fas fa-plus"></i> {{localize "AWEMMY.Item.Weapon"}}</button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{{!-- Equipment --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "AWEMMY.Item.Equipment"}}</legend>
|
||||||
|
<div class="item-list">
|
||||||
|
{{#each equipments as |item|}}
|
||||||
|
<div class="item-row" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
|
||||||
|
<div class="item-name">{{item.name}}</div>
|
||||||
|
<div class="item-qty">x{{item.system.quantity}}</div>
|
||||||
|
<div class="item-controls">
|
||||||
|
<a data-action="edit" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-edit"></i></a>
|
||||||
|
{{#if ../isEditMode}}
|
||||||
|
<a data-action="delete" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}"><i class="fas fa-trash"></i></a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{#if isEditMode}}
|
||||||
|
<div class="item-add">
|
||||||
|
<button type="button" data-action="createEquipment"><i class="fas fa-plus"></i> {{localize "AWEMMY.Item.Equipment"}}</button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
<section class="character-main character-main-{{#if isPlayMode}}play{{else}}edit{{/if}}">
|
||||||
|
|
||||||
|
{{!-- Header: image + name + basic stats --}}
|
||||||
|
<div class="actor-header">
|
||||||
|
<img class="actor-img" src="{{actor.img}}" data-edit="img" data-action="editImage" data-tooltip="{{actor.name}}" />
|
||||||
|
<div class="actor-identity">
|
||||||
|
{{formInput fields.name value=source.name classes="actor-name"}}
|
||||||
|
<div class="actor-details">
|
||||||
|
<div class="detail-item">
|
||||||
|
<label>{{localize "AWEMMY.Character.Level"}}</label>
|
||||||
|
{{formInput systemFields.level value=system.level disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<label>{{localize "AWEMMY.Character.Stride"}}</label>
|
||||||
|
{{formInput systemFields.stride value=system.stride disabled=isPlayMode}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="actor-stats">
|
||||||
|
{{!-- HP --}}
|
||||||
|
<div class="resource-block">
|
||||||
|
<label>{{localize "AWEMMY.Character.HP"}}</label>
|
||||||
|
<div class="resource-values">
|
||||||
|
{{formInput systemFields.hp.fields.value value=system.hp.value classes="hp-value"}}
|
||||||
|
<span class="separator">/</span>
|
||||||
|
{{formInput systemFields.hp.fields.max value=system.hp.max disabled=isPlayMode classes="hp-max"}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Flow Points --}}
|
||||||
|
<div class="resource-block">
|
||||||
|
<label>{{localize "AWEMMY.Character.FlowPoints"}}</label>
|
||||||
|
<div class="resource-values">
|
||||||
|
{{formInput systemFields.flowPoints.fields.value value=system.flowPoints.value classes="fp-value"}}
|
||||||
|
</div>
|
||||||
|
{{#if isPlayMode}}
|
||||||
|
<div class="resource-stepper">
|
||||||
|
<button type="button" data-action="flowPointsPlus" data-tooltip="+1">+</button>
|
||||||
|
<button type="button" data-action="flowPointsMinus" data-tooltip="-1">−</button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sheet-controls">
|
||||||
|
<button type="button" data-action="toggleSheet" data-tooltip="{{#if isPlayMode}}{{localize 'AWEMMY.Sheet.EditMode'}}{{else}}{{localize 'AWEMMY.Sheet.PlayMode'}}{{/if}}">
|
||||||
|
{{#if isPlayMode}}<i class="fa-solid fa-lock"></i>{{else}}<i class="fa-solid fa-unlock"></i>{{/if}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Attributes Table --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>Attributes</legend>
|
||||||
|
<table class="attributes-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Attribute</th>
|
||||||
|
<th>{{localize "AWEMMY.Character.BoostLevel"}}</th>
|
||||||
|
<th>{{localize "AWEMMY.Character.Mod"}}</th>
|
||||||
|
<th>{{localize "AWEMMY.Character.DC"}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label rollable" data-attribute-id="agility">{{localize "AWEMMY.Attribute.Agility"}} <i class="fa-solid fa-dice-d20"></i></td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.agility.fields.boostLevel value=system.attributes.agility.boostLevel disabled=isPlayMode}}</td>
|
||||||
|
<td>{{system.attributes.agility.mod}}</td>
|
||||||
|
<td>{{system.attributes.agility.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label rollable" data-attribute-id="fitness">{{localize "AWEMMY.Attribute.Fitness"}} <i class="fa-solid fa-dice-d20"></i></td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.fitness.fields.boostLevel value=system.attributes.fitness.boostLevel disabled=isPlayMode}}</td>
|
||||||
|
<td>{{system.attributes.fitness.mod}}</td>
|
||||||
|
<td>{{system.attributes.fitness.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label rollable" data-attribute-id="awareness">{{localize "AWEMMY.Attribute.Awareness"}} <i class="fa-solid fa-dice-d20"></i></td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.awareness.fields.boostLevel value=system.attributes.awareness.boostLevel disabled=isPlayMode}}</td>
|
||||||
|
<td>{{system.attributes.awareness.mod}}</td>
|
||||||
|
<td>{{system.attributes.awareness.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label rollable" data-attribute-id="influence">{{localize "AWEMMY.Attribute.Influence"}} <i class="fa-solid fa-dice-d20"></i></td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.influence.fields.boostLevel value=system.attributes.influence.boostLevel disabled=isPlayMode}}</td>
|
||||||
|
<td>{{system.attributes.influence.mod}}</td>
|
||||||
|
<td>{{system.attributes.influence.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{{!-- Abilities --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "AWEMMY.Item.Ability"}}</legend>
|
||||||
|
<div class="item-list">
|
||||||
|
{{#each abilities as |item|}}
|
||||||
|
<div class="item-row" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-tooltip="{{item.name}}" />
|
||||||
|
<div class="item-name">{{item.name}}</div>
|
||||||
|
<div class="item-cost">{{item.system.cost}}</div>
|
||||||
|
<div class="item-controls">
|
||||||
|
<a data-action="edit" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-tooltip="Edit"><i class="fas fa-edit"></i></a>
|
||||||
|
{{#if ../isEditMode}}
|
||||||
|
<a data-action="delete" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-tooltip="Delete"><i class="fas fa-trash"></i></a>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
|
{{#if isEditMode}}
|
||||||
|
<div class="item-add">
|
||||||
|
<button type="button" data-action="createAbility"><i class="fas fa-plus"></i> {{localize "AWEMMY.Item.Ability"}}</button>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<div class="awemmy-chat">
|
||||||
|
<div class="chat-roll-header">
|
||||||
|
<div class="chat-roll-label">{{flavor}}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#unless isPrivate}}
|
||||||
|
<div class="roll-result">{{total}}</div>
|
||||||
|
|
||||||
|
{{#if outcome}}
|
||||||
|
<div class="outcome-badge {{outcome}}">
|
||||||
|
{{#if (eq outcome "criticalSuccess")}}
|
||||||
|
<i class="fa-solid fa-star"></i> {{localize "AWEMMY.Roll.CriticalSuccess"}}
|
||||||
|
{{else if (eq outcome "success")}}
|
||||||
|
<i class="fa-solid fa-circle-check"></i> {{localize "AWEMMY.Roll.Success"}}
|
||||||
|
{{else if (eq outcome "failure")}}
|
||||||
|
<i class="fa-solid fa-circle-xmark"></i> {{localize "AWEMMY.Roll.Failure"}}
|
||||||
|
{{else if (eq outcome "criticalFailure")}}
|
||||||
|
<i class="fa-solid fa-skull"></i> {{localize "AWEMMY.Roll.CriticalFailure"}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<div class="private-result">
|
||||||
|
<i class="fa-solid fa-eye-slash"></i> Private Roll
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
<section class="creature-main">
|
||||||
|
|
||||||
|
<div class="actor-header">
|
||||||
|
<img class="actor-img" src="{{actor.img}}" data-edit="img" data-action="editImage" data-tooltip="{{actor.name}}" />
|
||||||
|
<div class="actor-identity">
|
||||||
|
{{formInput fields.name value=source.name classes="actor-name"}}
|
||||||
|
<div class="actor-details">
|
||||||
|
<div class="detail-item">
|
||||||
|
<label>{{localize "AWEMMY.Character.Level"}}</label>
|
||||||
|
{{formInput systemFields.level value=system.level}}
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<label>{{localize "AWEMMY.Character.Stride"}}</label>
|
||||||
|
{{formInput systemFields.stride value=system.stride}}
|
||||||
|
</div>
|
||||||
|
<div class="detail-item">
|
||||||
|
<label>{{localize "AWEMMY.Character.HP"}}</label>
|
||||||
|
{{formInput systemFields.hp.fields.value value=system.hp.value}}
|
||||||
|
<span>/</span>
|
||||||
|
{{formInput systemFields.hp.fields.max value=system.hp.max}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sheet-controls">
|
||||||
|
<button type="button" data-action="toggleSheet">
|
||||||
|
{{#if isPlayMode}}<i class="fa-solid fa-lock"></i>{{else}}<i class="fa-solid fa-unlock"></i>{{/if}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Attributes --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>Attributes</legend>
|
||||||
|
<table class="attributes-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Attribute</th>
|
||||||
|
<th>{{localize "AWEMMY.Character.Mod"}}</th>
|
||||||
|
<th>{{localize "AWEMMY.Character.DC"}}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label">{{localize "AWEMMY.Attribute.Agility"}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.agility.fields.mod value=system.attributes.agility.mod}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.agility.fields.dc value=system.attributes.agility.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label">{{localize "AWEMMY.Attribute.Fitness"}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.fitness.fields.mod value=system.attributes.fitness.mod}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.fitness.fields.dc value=system.attributes.fitness.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label">{{localize "AWEMMY.Attribute.Awareness"}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.awareness.fields.mod value=system.attributes.awareness.mod}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.awareness.fields.dc value=system.attributes.awareness.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="attr-label">{{localize "AWEMMY.Attribute.Influence"}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.influence.fields.mod value=system.attributes.influence.mod}}</td>
|
||||||
|
<td>{{formInput systemFields.attributes.fields.influence.fields.dc value=system.attributes.influence.dc}}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{{!-- Description --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
{{!-- Eureka Rubric --}}
|
||||||
|
<fieldset>
|
||||||
|
<legend>{{localize "AWEMMY.Creature.EurekaRubric"}}</legend>
|
||||||
|
<div class="eureka-rubric">
|
||||||
|
<div class="eureka-row">
|
||||||
|
<label>{{localize "AWEMMY.Creature.Claims"}}</label>
|
||||||
|
{{formInput systemFields.eurekaClaims value=system.eurekaClaims}}
|
||||||
|
</div>
|
||||||
|
<div class="eureka-row">
|
||||||
|
<label>{{localize "AWEMMY.Creature.Evidence"}}</label>
|
||||||
|
{{formInput systemFields.eurekaEvidence value=system.eurekaEvidence}}
|
||||||
|
</div>
|
||||||
|
<div class="eureka-row">
|
||||||
|
<label>Threshold 1</label>
|
||||||
|
{{formInput systemFields.eurekaThreshold1 value=system.eurekaThreshold1}}
|
||||||
|
</div>
|
||||||
|
<div class="eureka-row">
|
||||||
|
<label>Threshold 2</label>
|
||||||
|
{{formInput systemFields.eurekaThreshold2 value=system.eurekaThreshold2}}
|
||||||
|
</div>
|
||||||
|
<div class="eureka-row">
|
||||||
|
<label>{{localize "AWEMMY.Creature.Hints"}}</label>
|
||||||
|
{{formInput systemFields.eurekaHints value=system.eurekaHints}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<section>
|
||||||
|
<div class="item-header">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
||||||
|
{{formInput fields.name value=source.name}}
|
||||||
|
</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Equipment.Quantity"}}</label>
|
||||||
|
{{formInput systemFields.quantity value=system.quantity}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Equipment.Weight"}}</label>
|
||||||
|
{{formInput systemFields.weight value=system.weight}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<section>
|
||||||
|
<div class="item-header">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
||||||
|
{{formInput fields.name value=source.name}}
|
||||||
|
</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Field.KeyAttribute"}}</label>
|
||||||
|
{{formField systemFields.keyAttribute value=system.keyAttribute localize=true}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Field.KnowledgeBonus"}}</label>
|
||||||
|
{{formInput systemFields.knowledgeBonus value=system.knowledgeBonus}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<section>
|
||||||
|
<div class="item-header">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
||||||
|
{{formInput fields.name value=source.name}}
|
||||||
|
</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Kit.Field"}}</label>
|
||||||
|
{{formInput systemFields.fieldName value=system.fieldName}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Kit.Charges"}}</label>
|
||||||
|
{{formInput systemFields.charges.fields.value value=system.charges.value}}
|
||||||
|
<span>/</span>
|
||||||
|
{{formInput systemFields.charges.fields.max value=system.charges.max}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<section>
|
||||||
|
<div class="item-header">
|
||||||
|
<img class="item-img" src="{{item.img}}" data-edit="img" data-action="editImage" data-tooltip="{{item.name}}" />
|
||||||
|
{{formInput fields.name value=source.name}}
|
||||||
|
</div>
|
||||||
|
<div class="item-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Weapon.AttackAttribute"}}</label>
|
||||||
|
{{formField systemFields.attackAttribute value=system.attackAttribute localize=true}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Weapon.Damage"}}</label>
|
||||||
|
{{formInput systemFields.damageFormula value=system.damageFormula}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Weapon.DamageType"}}</label>
|
||||||
|
{{formInput systemFields.damageType value=system.damageType}}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{localize "AWEMMY.Weapon.Range"}}</label>
|
||||||
|
{{formInput systemFields.range value=system.range}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<legend>Description</legend>
|
||||||
|
{{formInput
|
||||||
|
systemFields.description
|
||||||
|
enriched=enrichedDescription
|
||||||
|
value=system.description
|
||||||
|
name="system.description"
|
||||||
|
toggled=true
|
||||||
|
}}
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
Reference in New Issue
Block a user