mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-15 13:11:08 +01:00
Merge branch 'refactor/84-data-models-structure' into feature/109-LevelUp-Followup
This commit is contained in:
commit
7a090ea203
81 changed files with 2465 additions and 1032 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,4 +3,5 @@ node_modules
|
||||||
/packs
|
/packs
|
||||||
Build
|
Build
|
||||||
/build
|
/build
|
||||||
foundry
|
foundry
|
||||||
|
styles/daggerheart.css
|
||||||
1
daggerheart.d.ts
vendored
1
daggerheart.d.ts
vendored
|
|
@ -1,4 +1,3 @@
|
||||||
import './module/_types';
|
|
||||||
import '@client/global.mjs';
|
import '@client/global.mjs';
|
||||||
import Canvas from '@client/canvas/board.mjs';
|
import Canvas from '@client/canvas/board.mjs';
|
||||||
|
|
||||||
|
|
|
||||||
116
daggerheart.mjs
116
daggerheart.mjs
|
|
@ -19,6 +19,7 @@ globalThis.SYSTEM = SYSTEM;
|
||||||
|
|
||||||
Hooks.once('init', () => {
|
Hooks.once('init', () => {
|
||||||
CONFIG.daggerheart = SYSTEM;
|
CONFIG.daggerheart = SYSTEM;
|
||||||
|
|
||||||
game.system.api = {
|
game.system.api = {
|
||||||
applications,
|
applications,
|
||||||
models,
|
models,
|
||||||
|
|
@ -134,43 +135,24 @@ Hooks.on(socketEvent.GMUpdate, async (action, uuid, update) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderDualityButton = async event => {
|
const renderDualityButton = async event => {
|
||||||
const button = event.currentTarget;
|
const button = event.currentTarget,
|
||||||
const attributeValue = button.dataset.attribute?.toLowerCase();
|
traitValue = button.dataset.trait?.toLowerCase(),
|
||||||
|
target = getCommandTarget();
|
||||||
const target = getCommandTarget();
|
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
const rollModifier = attributeValue ? target.system.attributes[attributeValue].data.value : null;
|
const config = {
|
||||||
const { roll, hope, fear, advantage, disadvantage, modifiers } = await target.diceRoll({
|
event: event,
|
||||||
title: button.dataset.label,
|
title: button.dataset.title,
|
||||||
value: rollModifier
|
roll: {
|
||||||
});
|
modifier: traitValue ? target.system.traits[traitValue].value : null,
|
||||||
|
label: button.dataset.label,
|
||||||
const systemData = new DHDualityRoll({
|
type: button.dataset.actionType ?? null // Need check
|
||||||
title: button.dataset.label,
|
},
|
||||||
origin: target.id,
|
chatMessage: {
|
||||||
roll: roll._formula,
|
template: 'systems/daggerheart/templates/chat/duality-roll.hbs'
|
||||||
modifiers: modifiers,
|
}
|
||||||
hope: hope,
|
|
||||||
fear: fear,
|
|
||||||
advantage: advantage,
|
|
||||||
disadvantage: disadvantage
|
|
||||||
});
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
const msgData = {
|
|
||||||
type: 'dualityRoll',
|
|
||||||
sound: CONFIG.sounds.dice,
|
|
||||||
system: systemData,
|
|
||||||
user: game.user.id,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
|
||||||
systemData
|
|
||||||
),
|
|
||||||
rolls: [roll]
|
|
||||||
};
|
};
|
||||||
|
await target.diceRoll(config);
|
||||||
await cls.create(msgData);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Hooks.on('renderChatMessageHTML', (_, element) => {
|
Hooks.on('renderChatMessageHTML', (_, element) => {
|
||||||
|
|
@ -199,64 +181,54 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributeValue = rollCommand.attribute?.toLowerCase();
|
const traitValue = rollCommand.trait?.toLowerCase();
|
||||||
|
const advantageState = rollCommand.advantage ? true : rollCommand.disadvantage ? false : null;
|
||||||
|
|
||||||
// Target not required if an attribute is not used.
|
// Target not required if an attribute is not used.
|
||||||
const target = attributeValue ? getCommandTarget() : undefined;
|
const target = traitValue ? getCommandTarget() : undefined;
|
||||||
if (target || !attributeValue) {
|
if (target || !traitValue) {
|
||||||
new Promise(async (resolve, reject) => {
|
new Promise(async (resolve, reject) => {
|
||||||
const attribute = target ? target.system.attributes[attributeValue] : undefined;
|
const trait = target ? target.system.traits[traitValue] : undefined;
|
||||||
if (attributeValue && !attribute) {
|
if (traitValue && !trait) {
|
||||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.AttributeFaulty'));
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.AttributeFaulty'));
|
||||||
reject();
|
reject();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const title = attributeValue
|
const title = traitValue
|
||||||
? game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
? game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
||||||
ability: game.i18n.localize(abilities[attributeValue].label)
|
ability: game.i18n.localize(abilities[traitValue].label)
|
||||||
})
|
})
|
||||||
: game.i18n.localize('DAGGERHEART.General.Duality');
|
: game.i18n.localize('DAGGERHEART.General.Duality');
|
||||||
|
|
||||||
const hopeAndFearRoll = `1${rollCommand.hope ?? 'd12'}+1${rollCommand.fear ?? 'd12'}`;
|
const hopeAndFearRoll = `1${rollCommand.hope ?? 'd12'}+1${rollCommand.fear ?? 'd12'}`;
|
||||||
const advantageRoll = `${rollCommand.advantage && !rollCommand.disadvantage ? '+d6' : rollCommand.disadvantage && !rollCommand.advantage ? '-d6' : ''}`;
|
const advantageRoll = `${advantageState === true ? '+d6' : advantageState === false ? '-d6' : ''}`;
|
||||||
const attributeRoll = `${attribute?.data?.value ? `${attribute.data.value > 0 ? `+${attribute.data.value}` : `${attribute.data.value}`}` : ''}`;
|
const attributeRoll = `${trait?.value ? `${trait.value > 0 ? `+${trait.value}` : `${trait.value}`}` : ''}`;
|
||||||
const roll = new Roll(`${hopeAndFearRoll}${advantageRoll}${attributeRoll}`);
|
const roll = await Roll.create(`${hopeAndFearRoll}${advantageRoll}${attributeRoll}`).evaluate();
|
||||||
await roll.evaluate();
|
|
||||||
|
|
||||||
setDiceSoNiceForDualityRoll(
|
setDiceSoNiceForDualityRoll(roll, advantageState);
|
||||||
roll,
|
|
||||||
rollCommand.advantage && !rollCommand.disadvantage,
|
|
||||||
rollCommand.disadvantage && !rollCommand.advantage
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
roll,
|
roll,
|
||||||
attribute: attribute
|
trait: trait
|
||||||
? {
|
? {
|
||||||
value: attribute.data.value,
|
value: trait.value,
|
||||||
label: `${game.i18n.localize(abilities[attributeValue].label)} ${attribute.data.value >= 0 ? `+` : ``}${attribute.data.value}`
|
label: `${game.i18n.localize(abilities[traitValue].label)} ${trait.value >= 0 ? `+` : ``}${trait.value}`
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
title
|
title
|
||||||
});
|
});
|
||||||
}).then(async ({ roll, attribute, title }) => {
|
}).then(async ({ roll, trait, title }) => {
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const systemData = new DHDualityRoll({
|
const systemData = new DHDualityRoll({
|
||||||
title: title,
|
title: title,
|
||||||
origin: target?.id,
|
origin: target?.id,
|
||||||
roll: roll._formula,
|
roll: roll,
|
||||||
modifiers: attribute ? [attribute] : [],
|
modifiers: trait ? [trait] : [],
|
||||||
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
|
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
|
||||||
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
|
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
|
||||||
advantage:
|
advantage: advantageState !== null ? { dice: 'd6', value: roll.dice[2].total } : undefined,
|
||||||
rollCommand.advantage && !rollCommand.disadvantage
|
advantageState
|
||||||
? { dice: 'd6', value: roll.dice[2].total }
|
|
||||||
: undefined,
|
|
||||||
disadvantage:
|
|
||||||
rollCommand.disadvantage && !rollCommand.advantage
|
|
||||||
? { dice: 'd6', value: roll.dice[2].total }
|
|
||||||
: undefined
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const msgData = {
|
const msgData = {
|
||||||
|
|
@ -264,10 +236,7 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
sound: CONFIG.sounds.dice,
|
sound: CONFIG.sounds.dice,
|
||||||
system: systemData,
|
system: systemData,
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||||
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
|
||||||
systemData
|
|
||||||
),
|
|
||||||
rolls: [roll]
|
rolls: [roll]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -301,6 +270,15 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/components/card-preview.hbs',
|
'systems/daggerheart/templates/components/card-preview.hbs',
|
||||||
'systems/daggerheart/templates/views/levelup/parts/selectable-card-preview.hbs',
|
'systems/daggerheart/templates/views/levelup/parts/selectable-card-preview.hbs',
|
||||||
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',
|
||||||
'systems/daggerheart/templates/ui/combat/combatTrackerSection.hbs'
|
'systems/daggerheart/templates/ui/combat/combatTrackerSection.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/damage.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/healing.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/resource.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/uuid.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/uses.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/roll.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/cost.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/range-target.hbs',
|
||||||
|
'systems/daggerheart/templates/views/actionTypes/effect.hbs'
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
45
lang/en.json
45
lang/en.json
|
|
@ -146,6 +146,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"General": {
|
"General": {
|
||||||
|
"Name": "Name",
|
||||||
"Hope": "Hope",
|
"Hope": "Hope",
|
||||||
"Fear": "Fear",
|
"Fear": "Fear",
|
||||||
"Duality": "Duality",
|
"Duality": "Duality",
|
||||||
|
|
@ -487,6 +488,10 @@
|
||||||
"twoHanded": "Two-Handed"
|
"twoHanded": "Two-Handed"
|
||||||
},
|
},
|
||||||
"Range": {
|
"Range": {
|
||||||
|
"self": {
|
||||||
|
"name": "Self",
|
||||||
|
"description": "means yourself."
|
||||||
|
},
|
||||||
"melee": {
|
"melee": {
|
||||||
"name": "Melee",
|
"name": "Melee",
|
||||||
"description": "means a character is within touching distance of the target. PCs can generally touch targets up to a few feet away from them, but melee range may be greater for especially large NPCs."
|
"description": "means a character is within touching distance of the target. PCs can generally touch targets up to a few feet away from them, but melee range may be greater for especially large NPCs."
|
||||||
|
|
@ -1293,10 +1298,48 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"Tooltip": {
|
"Tooltip": {
|
||||||
"openItemWorld": "Open Item World",
|
"openItemWorld": "Open Item World",
|
||||||
"delete": "Delete"
|
"delete": "Delete"
|
||||||
|
},
|
||||||
|
"Actions": {
|
||||||
|
"Types": {
|
||||||
|
"Attack": {
|
||||||
|
"Name": "Attack"
|
||||||
|
},
|
||||||
|
"Spellcast": {
|
||||||
|
"Name": "Spellcast"
|
||||||
|
},
|
||||||
|
"Resource": {
|
||||||
|
"Name": "Resource"
|
||||||
|
},
|
||||||
|
"Damage": {
|
||||||
|
"Name": "Damage"
|
||||||
|
},
|
||||||
|
"Healing": {
|
||||||
|
"Name": "Healing"
|
||||||
|
},
|
||||||
|
"Summon": {
|
||||||
|
"Name": "Summon"
|
||||||
|
},
|
||||||
|
"Effect": {
|
||||||
|
"Name": "Effect"
|
||||||
|
},
|
||||||
|
"Macro": {
|
||||||
|
"Name": "Macro"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RollTypes": {
|
||||||
|
"ability": {
|
||||||
|
"name": "Ability"
|
||||||
|
},
|
||||||
|
"weapon": {
|
||||||
|
"name": "Weapon"
|
||||||
|
},
|
||||||
|
"spellcast": {
|
||||||
|
"name": "SpellCast"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
module/_types.d.ts
vendored
0
module/_types.d.ts
vendored
|
|
@ -13,3 +13,5 @@ export { default as DhpArmor } from './sheets/items/armor.mjs';
|
||||||
export { default as DhpChatMessage } from './chatMessage.mjs';
|
export { default as DhpChatMessage } from './chatMessage.mjs';
|
||||||
export { default as DhpEnvironment } from './sheets/environment.mjs';
|
export { default as DhpEnvironment } from './sheets/environment.mjs';
|
||||||
export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs';
|
export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs';
|
||||||
|
|
||||||
|
export * as pseudoDocumentSheet from './sheets/pseudo-documents/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@ import DHDualityRoll from '../data/chat-message/dualityRoll.mjs';
|
||||||
|
|
||||||
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
export default class DhpChatMessage extends foundry.documents.ChatMessage {
|
||||||
async renderHTML() {
|
async renderHTML() {
|
||||||
|
if (this.type === 'dualityRoll' || this.type === 'adversaryRoll') {
|
||||||
|
this.content = await foundry.applications.handlebars.renderTemplate(this.content, this.system);
|
||||||
|
}
|
||||||
|
|
||||||
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
/* We can change to fully implementing the renderHTML function if needed, instead of augmenting it. */
|
||||||
const html = await super.renderHTML();
|
const html = await super.renderHTML();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
import DaggerheartSheet from '../sheets/daggerheart-sheet.mjs';
|
||||||
|
|
||||||
const { ApplicationV2 } = foundry.applications.api;
|
const { ApplicationV2 } = foundry.applications.api;
|
||||||
export default class DaggerheartActionConfig extends DaggerheartSheet(ApplicationV2) {
|
export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
||||||
constructor(action) {
|
constructor(action) {
|
||||||
super({});
|
super({});
|
||||||
|
|
||||||
|
|
@ -9,21 +9,25 @@ export default class DaggerheartActionConfig extends DaggerheartSheet(Applicatio
|
||||||
this.openSection = null;
|
this.openSection = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get title(){
|
|
||||||
// return `Action - ${this.action.name}`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
id: 'daggerheart-action',
|
id: 'daggerheart-action',
|
||||||
classes: ['daggerheart', 'views', 'action'],
|
classes: ['daggerheart', 'views', 'action'],
|
||||||
position: { width: 600, height: 'auto' },
|
position: { width: 600, height: 'auto' },
|
||||||
actions: {
|
actions: {
|
||||||
toggleSection: this.toggleSection
|
toggleSection: this.toggleSection,
|
||||||
|
addEffect: this.addEffect,
|
||||||
|
removeEffect: this.removeEffect,
|
||||||
|
addElement: this.addElement,
|
||||||
|
removeElement: this.removeElement,
|
||||||
|
editEffect: this.editEffect,
|
||||||
|
addDamage: this.addDamage,
|
||||||
|
removeDamage: this.removeDamage
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
handler: this.updateForm,
|
handler: this.updateForm,
|
||||||
closeOnSubmit: true
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -36,16 +40,9 @@ export default class DaggerheartActionConfig extends DaggerheartSheet(Applicatio
|
||||||
|
|
||||||
_getTabs() {
|
_getTabs() {
|
||||||
const tabs = {
|
const tabs = {
|
||||||
effects: { active: true, cssClass: '', group: 'primary', id: 'effects', icon: null, label: 'Effects' },
|
base: { active: true, cssClass: '', group: 'primary', id: 'base', icon: null, label: 'Base' },
|
||||||
useage: { active: false, cssClass: '', group: 'primary', id: 'useage', icon: null, label: 'Useage' },
|
config: { active: false, cssClass: '', group: 'primary', id: 'config', icon: null, label: 'Configuration' },
|
||||||
conditions: {
|
effect: { active: false, cssClass: '', group: 'primary', id: 'effect', icon: null, label: 'Effect' }
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'conditions',
|
|
||||||
icon: null,
|
|
||||||
label: 'Conditions'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const v of Object.values(tabs)) {
|
for (const v of Object.values(tabs)) {
|
||||||
|
|
@ -58,9 +55,13 @@ export default class DaggerheartActionConfig extends DaggerheartSheet(Applicatio
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
const context = await super._prepareContext(_options, 'action');
|
const context = await super._prepareContext(_options, 'action');
|
||||||
|
context.source = this.action.toObject(false);
|
||||||
context.openSection = this.openSection;
|
context.openSection = this.openSection;
|
||||||
context.tabs = this._getTabs();
|
context.tabs = this._getTabs();
|
||||||
|
context.config = SYSTEM;
|
||||||
|
if (!!this.action.effects) context.effects = this.action.effects.map(e => this.action.item.effects.get(e._id));
|
||||||
|
if (this.action.damage?.hasOwnProperty('includeBase')) context.hasBaseDamage = !!this.action.parent.damage;
|
||||||
|
context.getRealIndex = this.getRealIndex.bind(this);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,15 +70,91 @@ export default class DaggerheartActionConfig extends DaggerheartSheet(Applicatio
|
||||||
this.render(true);
|
this.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
getRealIndex(index) {
|
||||||
const data = foundry.utils.expandObject(
|
const data = this.action.toObject(false);
|
||||||
foundry.utils.mergeObject(this.action.toObject(), foundry.utils.expandObject(formData.object))
|
return data.damage.parts.find(d => d.base) ? index - 1 : index;
|
||||||
);
|
|
||||||
const newActions = this.action.parent.actions.map(x => x.toObject());
|
|
||||||
if (!newActions.findSplice(x => x.id === data.id, data)) {
|
|
||||||
newActions.push(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.action.parent.parent.update({ 'system.actions': newActions });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_prepareSubmitData(event, formData) {
|
||||||
|
const submitData = foundry.utils.expandObject(formData.object);
|
||||||
|
// this.element.querySelectorAll("fieldset[disabled] :is(input, select)").forEach(input => {
|
||||||
|
// foundry.utils.setProperty(submitData, input.name, input.value);
|
||||||
|
// });
|
||||||
|
return submitData;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
const submitData = this._prepareSubmitData(event, formData),
|
||||||
|
data = foundry.utils.expandObject(foundry.utils.mergeObject(this.action.toObject(), submitData)),
|
||||||
|
newActions = this.action.parent.actions.map(x => x.toObject()); // Find better way
|
||||||
|
if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data);
|
||||||
|
const updates = await this.action.parent.parent.update({ 'system.actions': newActions });
|
||||||
|
if (!updates) return;
|
||||||
|
this.action = updates.system.actions[this.action.index];
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static addElement(event) {
|
||||||
|
const data = this.action.toObject(),
|
||||||
|
key = event.target.closest('.action-category-data').dataset.key;
|
||||||
|
if (!this.action[key]) return;
|
||||||
|
data[key].push({});
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeElement(event) {
|
||||||
|
const data = this.action.toObject(),
|
||||||
|
key = event.target.closest('.action-category-data').dataset.key,
|
||||||
|
index = event.target.dataset.index;
|
||||||
|
data[key].splice(index, 1);
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static addDamage(event) {
|
||||||
|
if (!this.action.damage.parts) return;
|
||||||
|
const data = this.action.toObject();
|
||||||
|
data.damage.parts.push({});
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeDamage(event) {
|
||||||
|
if (!this.action.damage.parts) return;
|
||||||
|
const data = this.action.toObject(),
|
||||||
|
index = event.target.dataset.index;
|
||||||
|
data.damage.parts.splice(index, 1);
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async addEffect(event) {
|
||||||
|
if (!this.action.effects) return;
|
||||||
|
const effectData = this._addEffectData.bind(this)(),
|
||||||
|
[created] = await this.action.item.createEmbeddedDocuments('ActiveEffect', [effectData], { render: false }),
|
||||||
|
data = this.action.toObject();
|
||||||
|
data.effects.push({ _id: created._id });
|
||||||
|
this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data for a newly created applied effect.
|
||||||
|
* @returns {object}
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
_addEffectData() {
|
||||||
|
return {
|
||||||
|
name: this.action.item.name,
|
||||||
|
img: this.action.item.img,
|
||||||
|
origin: this.action.item.uuid,
|
||||||
|
transfer: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static removeEffect(event) {
|
||||||
|
if (!this.action.effects) return;
|
||||||
|
const index = event.target.dataset.index,
|
||||||
|
effectId = this.action.effects[index]._id;
|
||||||
|
this.constructor.removeElement.bind(this)(event);
|
||||||
|
this.action.item.deleteEmbeddedDocuments('ActiveEffect', [effectId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static editEffect(event) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export default class NpcRollSelectionDialog extends HandlebarsApplicationMixin(A
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
return game.i18n.localize('DAGGERHEART.Application.Settings.Title');
|
return game.i18n.localize('DAGGERHEART.Application.RollSelection.Title');
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
hope: ['d12'],
|
hope: ['d12'],
|
||||||
fear: ['d12'],
|
fear: ['d12'],
|
||||||
advantage: null,
|
advantage: null,
|
||||||
disadvantage: null,
|
|
||||||
hopeResource: hopeResource
|
hopeResource: hopeResource
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -30,9 +29,8 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
height: 'auto'
|
height: 'auto'
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
|
updateIsAdvantage: this.updateIsAdvantage,
|
||||||
selectExperience: this.selectExperience,
|
selectExperience: this.selectExperience,
|
||||||
setAdvantage: this.setAdvantage,
|
|
||||||
setDisadvantage: this.setDisadvantage,
|
|
||||||
finish: this.finish
|
finish: this.finish
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
|
|
@ -61,7 +59,6 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
context.hope = this.data.hope;
|
context.hope = this.data.hope;
|
||||||
context.fear = this.data.fear;
|
context.fear = this.data.fear;
|
||||||
context.advantage = this.data.advantage;
|
context.advantage = this.data.advantage;
|
||||||
context.disadvantage = this.data.disadvantage;
|
|
||||||
context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] }));
|
context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] }));
|
||||||
context.hopeResource = this.data.hopeResource + 1;
|
context.hopeResource = this.data.hopeResource + 1;
|
||||||
|
|
||||||
|
|
@ -85,18 +82,10 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
static setAdvantage() {
|
static updateIsAdvantage(_, button) {
|
||||||
this.data.advantage = this.data.advantage ? null : 'd6';
|
const advantage = Boolean(button.dataset.advantage);
|
||||||
this.data.disadvantage = null;
|
this.data.advantage = this.data.advantage === advantage ? null : advantage;
|
||||||
|
this.render();
|
||||||
this.render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static setDisadvantage() {
|
|
||||||
this.data.advantage = null;
|
|
||||||
this.data.disadvantage = this.data.disadvantage ? null : 'd6';
|
|
||||||
|
|
||||||
this.render(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async finish() {
|
static async finish() {
|
||||||
|
|
|
||||||
|
|
@ -166,8 +166,6 @@ class DhpRangeSettings extends FormApplication {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerDHSettings = () => {
|
export const registerDHSettings = () => {
|
||||||
// const debouncedReload = foundry.utils.debounce(() => window.location.reload(), 100);
|
|
||||||
|
|
||||||
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray, {
|
game.settings.register(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray, {
|
||||||
name: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Name'),
|
name: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Name'),
|
||||||
hint: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Hint'),
|
hint: game.i18n.localize('DAGGERHEART.Settings.General.AbilityArray.Hint'),
|
||||||
|
|
@ -274,7 +272,7 @@ export const registerDHSettings = () => {
|
||||||
name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'),
|
name: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Name'),
|
||||||
hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'),
|
hint: game.i18n.localize('DAGGERHEART.Settings.DualityRollColor.Hint'),
|
||||||
scope: 'world',
|
scope: 'world',
|
||||||
config: true,
|
config: false,
|
||||||
type: Number,
|
type: Number,
|
||||||
choices: Object.values(DualityRollColor),
|
choices: Object.values(DualityRollColor),
|
||||||
default: DualityRollColor.colorful.value
|
default: DualityRollColor.colorful.value
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
|
||||||
|
|
||||||
static async save() {
|
static async save() {
|
||||||
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, this.settings.toObject());
|
await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance, this.settings.toObject());
|
||||||
|
const reload = await foundry.applications.api.DialogV2.confirm({
|
||||||
|
id: 'reload-world-confirm',
|
||||||
|
modal: true,
|
||||||
|
rejectClose: false,
|
||||||
|
window: { title: 'SETTINGS.ReloadPromptTitle' },
|
||||||
|
position: { width: 400 },
|
||||||
|
content: `<p>${game.i18n.localize('SETTINGS.ReloadPromptBody')}</p>`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (reload) {
|
||||||
|
await game.socket.emit('reload');
|
||||||
|
foundry.utils.debouncedReload();
|
||||||
|
}
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,70 +61,42 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async reactionRoll(event) {
|
static async reactionRoll(event) {
|
||||||
const { roll, diceResults, modifiers } = await this.actor.diceRoll(
|
const config = {
|
||||||
{ title: `${this.actor.name} - Reaction Roll`, value: 0 },
|
event: event,
|
||||||
event.shiftKey
|
title: `${this.actor.name} - Reaction Roll`,
|
||||||
);
|
roll: {
|
||||||
|
modifier: null,
|
||||||
const cls = getDocumentClass('ChatMessage');
|
type: 'reaction'
|
||||||
const systemData = {
|
},
|
||||||
roll: roll._formula,
|
chatMessage: {
|
||||||
total: roll._total,
|
type: 'adversaryRoll',
|
||||||
modifiers: modifiers,
|
template: 'systems/daggerheart/templates/chat/adversary-roll.hbs',
|
||||||
diceResults: diceResults
|
mute: true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const msg = new cls({
|
this.actor.diceRoll(config);
|
||||||
type: 'adversaryRoll',
|
|
||||||
system: systemData,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/adversary-roll.hbs',
|
|
||||||
systemData
|
|
||||||
),
|
|
||||||
rolls: [roll]
|
|
||||||
});
|
|
||||||
|
|
||||||
cls.create(msg.toObject());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async attackRoll() {
|
static async attackRoll(event) {
|
||||||
const { modifier, damage, name: attackName } = this.actor.system.attack;
|
const { modifier, damage, name: attackName } = this.actor.system.attack,
|
||||||
const { roll, dice, advantageState, modifiers } = await this.actor.diceRoll(
|
config = {
|
||||||
{ title: `${this.actor.name} - Attack Roll`, value: modifier },
|
event: event,
|
||||||
event.shiftKey
|
title: attackName,
|
||||||
);
|
roll: {
|
||||||
|
modifier: modifier,
|
||||||
const targets = Array.from(game.user.targets).map(x => ({
|
type: 'action'
|
||||||
id: x.id,
|
},
|
||||||
name: x.actor.name,
|
chatMessage: {
|
||||||
img: x.actor.img,
|
type: 'adversaryRoll',
|
||||||
difficulty: x.actor.system.difficulty,
|
template: 'systems/daggerheart/templates/chat/adversary-attack-roll.hbs'
|
||||||
evasion: x.actor.system.evasion
|
},
|
||||||
}));
|
damage: {
|
||||||
|
value: damage.value,
|
||||||
const cls = getDocumentClass('ChatMessage');
|
type: damage.type
|
||||||
const systemData = {
|
},
|
||||||
title: attackName,
|
checkTarget: true
|
||||||
origin: this.document.id,
|
};
|
||||||
roll: roll._formula,
|
this.actor.diceRoll(config);
|
||||||
advantageState,
|
|
||||||
total: roll._total,
|
|
||||||
modifiers: modifiers,
|
|
||||||
dice: dice,
|
|
||||||
targets: targets,
|
|
||||||
damage: { value: damage.value, type: damage.type }
|
|
||||||
};
|
|
||||||
const msg = new cls({
|
|
||||||
type: 'adversaryRoll',
|
|
||||||
sound: CONFIG.sounds.dice,
|
|
||||||
system: systemData,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/adversary-attack-roll.hbs',
|
|
||||||
systemData
|
|
||||||
),
|
|
||||||
rolls: [roll]
|
|
||||||
});
|
|
||||||
|
|
||||||
cls.create(msg.toObject());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addExperience() {
|
static async addExperience() {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
selectAncestry: this.selectAncestry,
|
selectAncestry: this.selectAncestry,
|
||||||
selectCommunity: this.selectCommunity,
|
selectCommunity: this.selectCommunity,
|
||||||
viewObject: this.viewObject,
|
viewObject: this.viewObject,
|
||||||
|
useItem: this.useItem,
|
||||||
useFeature: this.useFeature,
|
useFeature: this.useFeature,
|
||||||
takeShortRest: this.takeShortRest,
|
takeShortRest: this.takeShortRest,
|
||||||
takeLongRest: this.takeLongRest,
|
takeLongRest: this.takeLongRest,
|
||||||
|
|
@ -150,6 +151,10 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
super._attachPartListeners(partId, htmlElement, options);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
htmlElement.querySelector('.level-value').addEventListener('change', this.onLevelChange.bind(this));
|
htmlElement.querySelector('.level-value').addEventListener('change', this.onLevelChange.bind(this));
|
||||||
|
// To Remove when ContextMenu Handler is made
|
||||||
|
htmlElement
|
||||||
|
.querySelectorAll('[data-item-id]')
|
||||||
|
.forEach(element => element.addEventListener('contextmenu', this.editItem.bind(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
async _prepareContext(_options) {
|
||||||
|
|
@ -280,7 +285,24 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static async rollAttribute(event, button) {
|
static async rollAttribute(event, button) {
|
||||||
const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll(
|
const abilityLabel = game.i18n.localize(abilities[button.dataset.attribute].label);
|
||||||
|
const config = {
|
||||||
|
event: event,
|
||||||
|
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
||||||
|
ability: abilityLabel
|
||||||
|
}),
|
||||||
|
roll: {
|
||||||
|
label: abilityLabel,
|
||||||
|
modifier: button.dataset.value
|
||||||
|
},
|
||||||
|
chatMessage: {
|
||||||
|
template: 'systems/daggerheart/templates/chat/duality-roll.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.document.diceRoll(config);
|
||||||
|
|
||||||
|
// Delete when new roll logic test done
|
||||||
|
/* const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll(
|
||||||
{ title: game.i18n.localize(abilities[button.dataset.attribute].label), value: button.dataset.value },
|
{ title: game.i18n.localize(abilities[button.dataset.attribute].label), value: button.dataset.value },
|
||||||
event.shiftKey
|
event.shiftKey
|
||||||
);
|
);
|
||||||
|
|
@ -310,7 +332,7 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
systemContent
|
systemContent
|
||||||
),
|
),
|
||||||
rolls: [roll]
|
rolls: [roll]
|
||||||
});
|
}); */
|
||||||
}
|
}
|
||||||
|
|
||||||
static async toggleMarks(_, button) {
|
static async toggleMarks(_, button) {
|
||||||
|
|
@ -348,51 +370,8 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
|
|
||||||
static async attackRoll(event, button) {
|
static async attackRoll(event, button) {
|
||||||
const weapon = await fromUuid(button.dataset.weapon);
|
const weapon = await fromUuid(button.dataset.weapon);
|
||||||
const damage = {
|
if (!weapon) return;
|
||||||
value: `${this.document.system.proficiency}${weapon.system.damage.value}`,
|
weapon.use(event);
|
||||||
type: weapon.system.damage.type
|
|
||||||
};
|
|
||||||
const modifier = this.document.system.traits[weapon.system.trait].value;
|
|
||||||
|
|
||||||
const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll(
|
|
||||||
{ title: game.i18n.localize(abilities[weapon.system.trait].label), value: modifier },
|
|
||||||
event.shiftKey
|
|
||||||
);
|
|
||||||
|
|
||||||
const targets = Array.from(game.user.targets).map(x => ({
|
|
||||||
id: x.id,
|
|
||||||
name: x.actor.name,
|
|
||||||
img: x.actor.img,
|
|
||||||
difficulty: x.actor.system.difficulty,
|
|
||||||
evasion: x.actor.system.evasion
|
|
||||||
}));
|
|
||||||
|
|
||||||
const systemData = new DHDualityRoll({
|
|
||||||
title: weapon.name,
|
|
||||||
origin: this.document.id,
|
|
||||||
roll: roll._formula,
|
|
||||||
modifiers: modifiers,
|
|
||||||
hope: hope,
|
|
||||||
fear: fear,
|
|
||||||
advantage: advantage,
|
|
||||||
disadvantage: disadvantage,
|
|
||||||
damage: damage,
|
|
||||||
targets: targets
|
|
||||||
});
|
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
|
||||||
const msg = new cls({
|
|
||||||
type: 'dualityRoll',
|
|
||||||
sound: CONFIG.sounds.dice,
|
|
||||||
system: systemData,
|
|
||||||
content: await foundry.applications.handlebars.renderTemplate(
|
|
||||||
'systems/daggerheart/templates/chat/attack-roll.hbs',
|
|
||||||
systemData
|
|
||||||
),
|
|
||||||
rolls: [roll]
|
|
||||||
});
|
|
||||||
|
|
||||||
await cls.create(msg.toObject());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static openLevelUp() {
|
static openLevelUp() {
|
||||||
|
|
@ -470,6 +449,12 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
(await game.packs.get('daggerheart.communities'))?.render(true);
|
(await game.packs.get('daggerheart.communities'))?.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static useItem(event) {
|
||||||
|
const uuid = event.target.closest('[data-item-id]').dataset.itemId,
|
||||||
|
item = this.document.items.find(i => i.uuid === uuid);
|
||||||
|
item.use(event);
|
||||||
|
}
|
||||||
|
|
||||||
static async viewObject(_, button) {
|
static async viewObject(_, button) {
|
||||||
const object = await fromUuid(button.dataset.value);
|
const object = await fromUuid(button.dataset.value);
|
||||||
if (!object) return;
|
if (!object) return;
|
||||||
|
|
@ -482,6 +467,16 @@ export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
object.sheet.render(true);
|
object.sheet.render(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
editItem(event) {
|
||||||
|
const uuid = event.target.closest('[data-item-id]').dataset.itemId,
|
||||||
|
item = this.document.items.find(i => i.uuid === uuid);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
if (item.sheet.editMode) item.sheet.editMode = false;
|
||||||
|
|
||||||
|
item.sheet.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
static async takeShortRest() {
|
static async takeShortRest() {
|
||||||
await new DhpDowntime(this.document, true).render(true);
|
await new DhpDowntime(this.document, true).render(true);
|
||||||
await this.minimize();
|
await this.minimize();
|
||||||
|
|
|
||||||
132
module/applications/sheets/item.mjs
Normal file
132
module/applications/sheets/item.mjs
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
import DhpApplicationMixin from './daggerheart-sheet.mjs';
|
||||||
|
import DHActionConfig from '../config/Action.mjs';
|
||||||
|
import { actionsTypes } from '../../data/_module.mjs';
|
||||||
|
|
||||||
|
export default function DHItemMixin(Base) {
|
||||||
|
return class DHItemSheetV2 extends DhpApplicationMixin(Base) {
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'sheet', 'item', 'dh-style'],
|
||||||
|
position: { width: 600 },
|
||||||
|
form: {
|
||||||
|
handler: this.updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
addAction: this.addAction,
|
||||||
|
editAction: this.editAction,
|
||||||
|
removeAction: this.removeAction
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static TABS = {
|
||||||
|
description: {
|
||||||
|
active: true,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'description',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'actions',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.Sheets.Feature.Tabs.Actions'
|
||||||
|
},
|
||||||
|
settings: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'settings',
|
||||||
|
icon: null,
|
||||||
|
label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.document = this.document;
|
||||||
|
context.config = CONFIG.daggerheart;
|
||||||
|
context.tabs = super._getTabs(this.constructor.TABS);
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
await this.document.update(formData.object);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async selectActionType() {
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/views/actionType.hbs',
|
||||||
|
{ types: SYSTEM.ACTIONS.actionTypes }
|
||||||
|
),
|
||||||
|
title = 'Select Action Type',
|
||||||
|
type = 'form',
|
||||||
|
data = {};
|
||||||
|
return Dialog.prompt({
|
||||||
|
title,
|
||||||
|
label: title,
|
||||||
|
content,
|
||||||
|
type,
|
||||||
|
callback: html => {
|
||||||
|
const form = html[0].querySelector('form'),
|
||||||
|
fd = new foundry.applications.ux.FormDataExtended(form);
|
||||||
|
foundry.utils.mergeObject(data, fd.object, { inplace: true });
|
||||||
|
// if (!data.name?.trim()) data.name = game.i18n.localize(SYSTEM.ACTIONS.actionTypes[data.type].name);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
rejectClose: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async addAction() {
|
||||||
|
const actionType = await DHItemSheetV2.selectActionType(),
|
||||||
|
actionIndexes = this.document.system.actions.map(x => x._id.split('-')[2]).sort((a, b) => a - b);
|
||||||
|
try {
|
||||||
|
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
|
||||||
|
action = new cls(
|
||||||
|
{
|
||||||
|
// id: `${this.document.id}-Action-${actionIndexes.length > 0 ? actionIndexes[0] + 1 : 1}`
|
||||||
|
_id: foundry.utils.randomID(),
|
||||||
|
type: actionType.type,
|
||||||
|
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name),
|
||||||
|
...cls.getSourceConfig(this.document)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
parent: this.document
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await this.document.update({ 'system.actions': [...this.document.system.actions, action] });
|
||||||
|
await new DHActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async editAction(_, button) {
|
||||||
|
const action = this.document.system.actions[button.dataset.index];
|
||||||
|
await new DHActionConfig(action).render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async removeAction(event, button) {
|
||||||
|
event.stopPropagation();
|
||||||
|
await this.document.update({
|
||||||
|
'system.actions': this.document.system.actions.filter(
|
||||||
|
(_, index) => index !== Number.parseInt(button.dataset.index)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,9 @@
|
||||||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
import DHItemSheetV2 from '../item.mjs';
|
||||||
|
|
||||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||||
export default class ArmorSheet extends DaggerheartSheet(ItemSheetV2) {
|
export default class ArmorSheet extends DHItemSheetV2(ItemSheetV2) {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
classes: ['armor'],
|
||||||
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'armor'],
|
|
||||||
position: { width: 600 },
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
},
|
|
||||||
dragDrop: [{ dragSelector: null, dropSelector: null }]
|
dragDrop: [{ dragSelector: null, dropSelector: null }]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -18,42 +11,13 @@ export default class ArmorSheet extends DaggerheartSheet(ItemSheetV2) {
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/items/armor/header.hbs' },
|
header: { template: 'systems/daggerheart/templates/sheets/items/armor/header.hbs' },
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
||||||
|
actions: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||||
|
scrollable: ['.actions']
|
||||||
|
},
|
||||||
settings: {
|
settings: {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
description: {
|
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'description',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'settings',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options);
|
|
||||||
context.document = this.document;
|
|
||||||
context.config = CONFIG.daggerheart;
|
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,23 @@
|
||||||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
import DHItemSheetV2 from '../item.mjs';
|
||||||
|
|
||||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||||
export default class ConsumableSheet extends DaggerheartSheet(ItemSheetV2) {
|
export default class ConsumableSheet extends DHItemSheetV2(ItemSheetV2) {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
classes: ['consumable'],
|
||||||
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'consumable'],
|
position: { width: 550 }
|
||||||
position: { width: 550 },
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/items/consumable/header.hbs' },
|
header: { template: 'systems/daggerheart/templates/sheets/items/consumable/header.hbs' },
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
||||||
|
actions: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||||
|
scrollable: ['.actions']
|
||||||
|
},
|
||||||
settings: {
|
settings: {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/consumable/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/consumable/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
description: {
|
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'description',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'settings',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options);
|
|
||||||
context.document = this.document;
|
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,10 @@
|
||||||
import DaggerheartAction from '../../../data/action.mjs';
|
import DHItemSheetV2 from '../item.mjs';
|
||||||
import DaggerheartActionConfig from '../../config/Action.mjs';
|
|
||||||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
|
||||||
|
|
||||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||||
export default class DomainCardSheet extends DaggerheartSheet(ItemSheetV2) {
|
export default class DomainCardSheet extends DHItemSheetV2(ItemSheetV2) {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
classes: ['domain-card'],
|
||||||
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'domain-card'],
|
position: { width: 450, height: 700 }
|
||||||
position: { width: 450, height: 700 },
|
|
||||||
actions: {
|
|
||||||
addAction: this.addAction,
|
|
||||||
editAction: this.editAction,
|
|
||||||
removeAction: this.removeAction
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
|
|
@ -33,74 +20,4 @@ export default class DomainCardSheet extends DaggerheartSheet(ItemSheetV2) {
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
description: {
|
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'description',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'actions',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Actions'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'settings',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options);
|
|
||||||
context.config = CONFIG.daggerheart;
|
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async addAction() {
|
|
||||||
const actionIndexes = this.document.system.actions.map(x => x.id.split('-')[2]).sort((a, b) => a - b);
|
|
||||||
const action = await new DaggerheartAction(
|
|
||||||
{
|
|
||||||
id: `${this.document.id}-Action-${actionIndexes.length > 0 ? actionIndexes[0] + 1 : 1}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
parent: this.document
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await this.document.update({ 'system.actions': [...this.document.system.actions, action] });
|
|
||||||
await new DaggerheartActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async editAction(_, button) {
|
|
||||||
const action = this.document.system.actions[button.dataset.index];
|
|
||||||
await new DaggerheartActionConfig(action).render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async removeAction(event, button) {
|
|
||||||
event.stopPropagation();
|
|
||||||
await this.document.update({
|
|
||||||
'system.actions': this.document.system.actions.filter(
|
|
||||||
(_, index) => index !== Number.parseInt(button.dataset.index)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import DaggerheartAction from '../../../data/action.mjs';
|
import DHItemSheetV2 from '../item.mjs';
|
||||||
import DaggerheartActionConfig from '../../config/Action.mjs';
|
|
||||||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
|
||||||
|
|
||||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||||
export default class FeatureSheet extends DaggerheartSheet(ItemSheetV2) {
|
export default class FeatureSheet extends DHItemSheetV2(ItemSheetV2) {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super(options);
|
super(options);
|
||||||
|
|
||||||
|
|
@ -11,22 +9,13 @@ export default class FeatureSheet extends DaggerheartSheet(ItemSheetV2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
|
||||||
id: 'daggerheart-feature',
|
id: 'daggerheart-feature',
|
||||||
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'feature'],
|
classes: ['feature'],
|
||||||
position: { width: 600, height: 600 },
|
position: { width: 600, height: 600 },
|
||||||
window: { resizable: true },
|
window: { resizable: true },
|
||||||
actions: {
|
actions: {
|
||||||
addEffect: this.addEffect,
|
addEffect: this.addEffect,
|
||||||
removeEffect: this.removeEffect,
|
removeEffect: this.removeEffect
|
||||||
addAction: this.addAction,
|
|
||||||
editAction: this.editAction,
|
|
||||||
removeAction: this.removeAction
|
|
||||||
},
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -49,30 +38,7 @@ export default class FeatureSheet extends DaggerheartSheet(ItemSheetV2) {
|
||||||
};
|
};
|
||||||
|
|
||||||
static TABS = {
|
static TABS = {
|
||||||
description: {
|
...super.TABS,
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'description',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'actions',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Actions'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'settings',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings'
|
|
||||||
},
|
|
||||||
effects: {
|
effects: {
|
||||||
active: false,
|
active: false,
|
||||||
cssClass: '',
|
cssClass: '',
|
||||||
|
|
@ -102,11 +68,6 @@ export default class FeatureSheet extends DaggerheartSheet(ItemSheetV2) {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
|
|
||||||
effectSelect(event) {
|
effectSelect(event) {
|
||||||
this.selectedEffectType = event.currentTarget.value;
|
this.selectedEffectType = event.currentTarget.value;
|
||||||
this.render(true);
|
this.render(true);
|
||||||
|
|
@ -130,26 +91,4 @@ export default class FeatureSheet extends DaggerheartSheet(ItemSheetV2) {
|
||||||
const path = `system.effects.-=${button.dataset.effect}`;
|
const path = `system.effects.-=${button.dataset.effect}`;
|
||||||
await this.item.update({ [path]: null });
|
await this.item.update({ [path]: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
static async addAction() {
|
|
||||||
const action = await new DaggerheartAction({ img: this.document.img }, { parent: this.document });
|
|
||||||
await this.document.update({ 'system.actions': [...this.document.system.actions, action] });
|
|
||||||
await new DaggerheartActionConfig(this.document.system.actions[this.document.system.actions.length - 1]).render(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async editAction(_, button) {
|
|
||||||
const action = this.document.system.actions[button.dataset.index];
|
|
||||||
await new DaggerheartActionConfig(action).render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async removeAction(event, button) {
|
|
||||||
event.stopPropagation();
|
|
||||||
await this.document.update({
|
|
||||||
'system.actions': this.document.system.actions.filter(
|
|
||||||
(_, index) => index !== Number.parseInt(button.dataset.index)
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,23 @@
|
||||||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
import DHItemSheetV2 from '../item.mjs';
|
||||||
|
|
||||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||||
export default class MiscellaneousSheet extends DaggerheartSheet(ItemSheetV2) {
|
export default class MiscellaneousSheet extends DHItemSheetV2(ItemSheetV2) {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
classes: ['miscellaneous'],
|
||||||
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'miscellaneous'],
|
position: { width: 550 }
|
||||||
position: { width: 550 },
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/items/miscellaneous/header.hbs' },
|
header: { template: 'systems/daggerheart/templates/sheets/items/miscellaneous/header.hbs' },
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
||||||
|
actions: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||||
|
scrollable: ['.actions']
|
||||||
|
},
|
||||||
settings: {
|
settings: {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/miscellaneous/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/miscellaneous/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
description: {
|
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'description',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'settings',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options);
|
|
||||||
context.document = this.document;
|
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,58 +1,22 @@
|
||||||
import DaggerheartSheet from '../daggerheart-sheet.mjs';
|
import DHItemSheetV2 from '../item.mjs';
|
||||||
|
|
||||||
const { ItemSheetV2 } = foundry.applications.sheets;
|
const { ItemSheetV2 } = foundry.applications.sheets;
|
||||||
export default class WeaponSheet extends DaggerheartSheet(ItemSheetV2) {
|
export default class WeaponSheet extends DHItemSheetV2(ItemSheetV2) {
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
classes: ['weapon']
|
||||||
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'weapon'],
|
|
||||||
position: { width: 600 },
|
|
||||||
form: {
|
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARTS = {
|
static PARTS = {
|
||||||
header: { template: 'systems/daggerheart/templates/sheets/items/weapon/header.hbs' },
|
header: { template: 'systems/daggerheart/templates/sheets/items/weapon/header.hbs' },
|
||||||
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
|
||||||
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
|
||||||
|
actions: {
|
||||||
|
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
|
||||||
|
scrollable: ['.actions']
|
||||||
|
},
|
||||||
settings: {
|
settings: {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static TABS = {
|
|
||||||
description: {
|
|
||||||
active: true,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'description',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
|
|
||||||
},
|
|
||||||
settings: {
|
|
||||||
active: false,
|
|
||||||
cssClass: '',
|
|
||||||
group: 'primary',
|
|
||||||
id: 'settings',
|
|
||||||
icon: null,
|
|
||||||
label: 'DAGGERHEART.Sheets.Feature.Tabs.Settings'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options);
|
|
||||||
context.document = this.document;
|
|
||||||
context.config = CONFIG.daggerheart;
|
|
||||||
context.tabs = super._getTabs(this.constructor.TABS);
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
|
||||||
await this.document.update(formData.object);
|
|
||||||
this.render();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
module/applications/sheets/pseudo-documents/_module.mjs
Normal file
1
module/applications/sheets/pseudo-documents/_module.mjs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export {default as PseudoDocumentSheet }from "./pseudo-documents-sheet.mjs";
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
|
export default class PseudoDocumentSheet extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
constructor(options) {
|
||||||
|
super(options);
|
||||||
|
this.#pseudoDocument = options.document;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The UUID of the associated pseudo-document
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get pseudoUuid() {
|
||||||
|
return this.pseudoDocument.uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pseudoDocument;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pseudo-document instance this sheet represents
|
||||||
|
* @type {object}
|
||||||
|
*/
|
||||||
|
get pseudoDocument() {
|
||||||
|
return this.#pseudoDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'sheet'],
|
||||||
|
position: { width: 600 },
|
||||||
|
form: {
|
||||||
|
handler: PseudoDocumentSheet.#onSubmitForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
},
|
||||||
|
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
header: { template: 'systems/daggerheart/templates/sheets/pseudo-documents/header.hbs' },
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
const document = this.pseudoDocument;
|
||||||
|
return Object.assign(context, {
|
||||||
|
document,
|
||||||
|
source: document._source,
|
||||||
|
editable: this.isEditable,
|
||||||
|
user: game.user,
|
||||||
|
rootId: this.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form submission handler
|
||||||
|
* @param {SubmitEvent | Event} event - The originating form submission or input change event
|
||||||
|
* @param {HTMLFormElement} form - The form element that was submitted
|
||||||
|
* @param {foundry.applications.ux.FormDataExtended} formData - Processed data for the submitted form
|
||||||
|
*/
|
||||||
|
static async #onSubmitForm(event, form, formData) {
|
||||||
|
const submitData = foundry.utils.expandObject(formData.object);
|
||||||
|
await this.pseudoDocument.update(submitData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,43 @@
|
||||||
export const actionTypes = {
|
export const actionTypes = {
|
||||||
|
attack: {
|
||||||
|
id: 'attack',
|
||||||
|
name: 'DAGGERHEART.Actions.Types.Attack.Name',
|
||||||
|
icon: 'fa-swords'
|
||||||
|
},
|
||||||
|
spellcast: {
|
||||||
|
id: 'spellcast',
|
||||||
|
name: 'DAGGERHEART.Actions.Types.Spellcast.Name',
|
||||||
|
icon: 'fa-book-sparkles'
|
||||||
|
},
|
||||||
|
healing: {
|
||||||
|
id: 'healing',
|
||||||
|
name: 'DAGGERHEART.Actions.Types.Healing.Name',
|
||||||
|
icon: 'fa-kit-medical'
|
||||||
|
},
|
||||||
|
resource: {
|
||||||
|
id: 'resource',
|
||||||
|
name: 'DAGGERHEART.Actions.Types.Resource.Name',
|
||||||
|
icon: 'fa-honey-pot'
|
||||||
|
},
|
||||||
damage: {
|
damage: {
|
||||||
id: 'damage',
|
id: 'damage',
|
||||||
name: 'DAGGERHEART.Effects.Types.Health.Name'
|
name: 'DAGGERHEART.Actions.Types.Damage.Name',
|
||||||
|
icon: 'fa-bone-break'
|
||||||
|
},
|
||||||
|
summon: {
|
||||||
|
id: 'summon',
|
||||||
|
name: 'DAGGERHEART.Actions.Types.Summon.Name',
|
||||||
|
icon: 'fa-ghost'
|
||||||
|
},
|
||||||
|
effect: {
|
||||||
|
id: 'effect',
|
||||||
|
name: 'DAGGERHEART.Actions.Types.Effect.Name',
|
||||||
|
icon: 'fa-person-rays'
|
||||||
|
},
|
||||||
|
macro: {
|
||||||
|
id: 'macro',
|
||||||
|
name: 'DAGGERHEART.Actions.Types.Macro.Name',
|
||||||
|
icon: 'fa-scroll'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
export const range = {
|
export const range = {
|
||||||
|
self: {
|
||||||
|
label: 'DAGGERHEART.Range.self.name',
|
||||||
|
description: 'DAGGERHEART.Range.self.description',
|
||||||
|
distance: 0
|
||||||
|
},
|
||||||
melee: {
|
melee: {
|
||||||
id: 'melee',
|
id: 'melee',
|
||||||
label: 'DAGGERHEART.Range.melee.name',
|
label: 'DAGGERHEART.Range.melee.name',
|
||||||
|
|
@ -248,6 +253,11 @@ export const diceTypes = {
|
||||||
d20: 'd20'
|
d20: 'd20'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const multiplierTypes = {
|
||||||
|
proficiency: 'Proficiency',
|
||||||
|
spellcast: 'Spellcast'
|
||||||
|
};
|
||||||
|
|
||||||
export const getDiceSoNicePresets = () => {
|
export const getDiceSoNicePresets = () => {
|
||||||
const { diceSoNice } = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance);
|
const { diceSoNice } = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance);
|
||||||
|
|
||||||
|
|
@ -312,3 +322,18 @@ export const abilityCosts = {
|
||||||
label: 'Stress'
|
label: 'Stress'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const rollTypes = {
|
||||||
|
weapon: {
|
||||||
|
id: 'weapon',
|
||||||
|
label: 'DAGGERHEART.RollTypes.weapon.name'
|
||||||
|
},
|
||||||
|
spellcast: {
|
||||||
|
id: 'spellcast',
|
||||||
|
label: 'DAGGERHEART.RollTypes.spellcast.name'
|
||||||
|
},
|
||||||
|
ability: {
|
||||||
|
id: 'ability',
|
||||||
|
label: 'DAGGERHEART.RollTypes.ability.name'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
17
module/config/pseudoConfig.mjs
Normal file
17
module/config/pseudoConfig.mjs
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { pseudoDocuments } from "../data/_module.mjs";
|
||||||
|
import { pseudoDocumentSheet } from "../applications/_module.mjs";
|
||||||
|
|
||||||
|
//CONFIG.daggerheart.pseudoDocuments
|
||||||
|
export default {
|
||||||
|
sheetClass: pseudoDocumentSheet.PseudoDocumentSheet,
|
||||||
|
feature: {
|
||||||
|
label: "DAGGERHEART.Feature.Label",
|
||||||
|
documentClass: pseudoDocuments.feature.BaseFeatureData,
|
||||||
|
types: {
|
||||||
|
weapon: {
|
||||||
|
label: "DAGGERHEART.Feature.Weapon.Label",
|
||||||
|
documentClass: pseudoDocuments.feature.WeaponFeature,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -5,6 +5,7 @@ import * as ITEM from './itemConfig.mjs';
|
||||||
import * as SETTINGS from './settingsConfig.mjs';
|
import * as SETTINGS from './settingsConfig.mjs';
|
||||||
import * as EFFECTS from './effectConfig.mjs';
|
import * as EFFECTS from './effectConfig.mjs';
|
||||||
import * as ACTIONS from './actionConfig.mjs';
|
import * as ACTIONS from './actionConfig.mjs';
|
||||||
|
import pseudoDocuments from "./pseudoConfig.mjs";
|
||||||
|
|
||||||
export const SYSTEM_ID = 'daggerheart';
|
export const SYSTEM_ID = 'daggerheart';
|
||||||
|
|
||||||
|
|
@ -16,5 +17,6 @@ export const SYSTEM = {
|
||||||
ITEM,
|
ITEM,
|
||||||
SETTINGS,
|
SETTINGS,
|
||||||
EFFECTS,
|
EFFECTS,
|
||||||
ACTIONS
|
ACTIONS,
|
||||||
|
pseudoDocuments
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,7 @@ export { default as DhCombatant } from './combatant.mjs';
|
||||||
|
|
||||||
export * as actors from './actor/_module.mjs';
|
export * as actors from './actor/_module.mjs';
|
||||||
export * as items from './item/_module.mjs';
|
export * as items from './item/_module.mjs';
|
||||||
|
export { actionsTypes } from './action/_module.mjs';
|
||||||
export * as messages from './chat-message/_modules.mjs';
|
export * as messages from './chat-message/_modules.mjs';
|
||||||
|
export * as fields from './fields/_module.mjs';
|
||||||
|
export * as pseudoDocuments from './pseudo-documents/_module.mjs';
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
export default class DaggerheartAction extends foundry.abstract.DataModel {
|
|
||||||
static defineSchema() {
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
return {
|
|
||||||
id: new fields.DocumentIdField(),
|
|
||||||
name: new fields.StringField({ initial: 'New Action' }),
|
|
||||||
damage: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.GENERAL.damageTypes, nullable: true, initial: null }),
|
|
||||||
value: new fields.StringField({})
|
|
||||||
}),
|
|
||||||
healing: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.GENERAL.healingTypes, nullable: true, initial: null }),
|
|
||||||
value: new fields.StringField()
|
|
||||||
}),
|
|
||||||
conditions: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
name: new fields.StringField(),
|
|
||||||
icon: new fields.StringField(),
|
|
||||||
description: new fields.StringField()
|
|
||||||
})
|
|
||||||
),
|
|
||||||
cost: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({ choices: SYSTEM.GENERAL.abilityCosts, nullable: true, initial: null }),
|
|
||||||
value: new fields.NumberField({ nullable: true, initial: null })
|
|
||||||
}),
|
|
||||||
target: new fields.SchemaField({
|
|
||||||
type: new fields.StringField({
|
|
||||||
choices: SYSTEM.ACTIONS.targetTypes,
|
|
||||||
initial: SYSTEM.ACTIONS.targetTypes.other.id
|
|
||||||
})
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
23
module/data/action/_module.mjs
Normal file
23
module/data/action/_module.mjs
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import {
|
||||||
|
DHAttackAction,
|
||||||
|
DHBaseAction,
|
||||||
|
DHDamageAction,
|
||||||
|
DHEffectAction,
|
||||||
|
DHHealingAction,
|
||||||
|
DHMacroAction,
|
||||||
|
DHResourceAction,
|
||||||
|
DHSpellCastAction,
|
||||||
|
DHSummonAction
|
||||||
|
} from './action.mjs';
|
||||||
|
|
||||||
|
export const actionsTypes = {
|
||||||
|
base: DHBaseAction,
|
||||||
|
attack: DHAttackAction,
|
||||||
|
spellcast: DHSpellCastAction,
|
||||||
|
resource: DHResourceAction,
|
||||||
|
damage: DHDamageAction,
|
||||||
|
healing: DHHealingAction,
|
||||||
|
summon: DHSummonAction,
|
||||||
|
effect: DHEffectAction,
|
||||||
|
macro: DHMacroAction
|
||||||
|
};
|
||||||
386
module/data/action/action.mjs
Normal file
386
module/data/action/action.mjs
Normal file
|
|
@ -0,0 +1,386 @@
|
||||||
|
import { abilities } from '../../config/actorConfig.mjs';
|
||||||
|
import { DHActionDiceData, DHDamageData, DHDamageField } from './actionDice.mjs';
|
||||||
|
|
||||||
|
export default class DHAction extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
return {
|
||||||
|
id: new fields.DocumentIdField(),
|
||||||
|
name: new fields.StringField({ initial: 'New Action' }),
|
||||||
|
damage: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({ choices: SYSTEM.GENERAL.damageTypes, nullable: true, initial: null }),
|
||||||
|
value: new fields.StringField({})
|
||||||
|
}),
|
||||||
|
healing: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({ choices: SYSTEM.GENERAL.healingTypes, nullable: true, initial: null }),
|
||||||
|
value: new fields.StringField()
|
||||||
|
}),
|
||||||
|
conditions: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
name: new fields.StringField(),
|
||||||
|
icon: new fields.StringField(),
|
||||||
|
description: new fields.StringField()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
cost: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({ choices: SYSTEM.GENERAL.abilityCosts, nullable: true, initial: null }),
|
||||||
|
value: new fields.NumberField({ nullable: true, initial: null })
|
||||||
|
}),
|
||||||
|
target: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: SYSTEM.ACTIONS.targetTypes,
|
||||||
|
initial: SYSTEM.ACTIONS.targetTypes.other.id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
/*
|
||||||
|
ToDo
|
||||||
|
- Apply ActiveEffect => Add to Chat message like Damage Button ?
|
||||||
|
- Add Drag & Drop for documentUUID field (Macro & Summon)
|
||||||
|
- Add optionnal Role for Healing ?
|
||||||
|
- Handle Roll result as part of formula if needed
|
||||||
|
- Target Check
|
||||||
|
- Cost Check
|
||||||
|
- Range Check
|
||||||
|
- Area of effect and measurement placement
|
||||||
|
- Auto use costs and action
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class DHBaseAction extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
_id: new fields.DocumentIdField(),
|
||||||
|
type: new fields.StringField({ initial: undefined, readonly: true, required: true }),
|
||||||
|
name: new fields.StringField({ initial: undefined }),
|
||||||
|
img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }),
|
||||||
|
actionType: new fields.StringField({ choices: SYSTEM.ITEM.actionTypes, initial: 'action', nullable: true }),
|
||||||
|
cost: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.abilityCosts,
|
||||||
|
nullable: false,
|
||||||
|
required: true,
|
||||||
|
initial: 'hope'
|
||||||
|
}),
|
||||||
|
value: new fields.NumberField({ nullable: true, initial: 1 }),
|
||||||
|
scalable: new fields.BooleanField({ initial: false }),
|
||||||
|
step: new fields.NumberField({ nullable: true, initial: null })
|
||||||
|
})
|
||||||
|
),
|
||||||
|
uses: new fields.SchemaField({
|
||||||
|
value: new fields.NumberField({ nullable: true, initial: null }),
|
||||||
|
max: new fields.NumberField({ nullable: true, initial: null }),
|
||||||
|
recovery: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.refreshTypes,
|
||||||
|
initial: null,
|
||||||
|
nullable: true
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
range: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.range,
|
||||||
|
required: true,
|
||||||
|
blank: false,
|
||||||
|
initial: 'self'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareData() {}
|
||||||
|
|
||||||
|
get index() {
|
||||||
|
return this.parent.actions.indexOf(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
get item() {
|
||||||
|
return this.parent.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
get actor() {
|
||||||
|
return this.item?.actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
get chatTemplate() {
|
||||||
|
return 'systems/daggerheart/templates/chat/attack-roll.hbs';
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRollType() {
|
||||||
|
return 'ability';
|
||||||
|
}
|
||||||
|
|
||||||
|
static getSourceConfig(parent) {
|
||||||
|
const updateSource = {};
|
||||||
|
updateSource.img ??= parent?.img ?? parent?.system?.img;
|
||||||
|
if (parent?.system?.trait) {
|
||||||
|
updateSource['roll'] = {
|
||||||
|
type: this.getRollType(),
|
||||||
|
trait: parent.system.trait
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (parent?.system?.range) {
|
||||||
|
updateSource['range'] = parent?.system?.range;
|
||||||
|
}
|
||||||
|
return updateSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
async use(event) {
|
||||||
|
if (this.roll.type && this.roll.trait) {
|
||||||
|
const modifierValue = this.actor.system.traits[this.roll.trait].value;
|
||||||
|
const config = {
|
||||||
|
event: event,
|
||||||
|
title: this.item.name,
|
||||||
|
roll: {
|
||||||
|
modifier: modifierValue,
|
||||||
|
label: game.i18n.localize(abilities[this.roll.trait].label),
|
||||||
|
type: this.actionType,
|
||||||
|
difficulty: this.roll?.difficulty
|
||||||
|
},
|
||||||
|
chatMessage: {
|
||||||
|
template: this.chatTemplate
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (this.target?.type) config.checkTarget = true;
|
||||||
|
if (this.damage.parts.length) {
|
||||||
|
config.damage = {
|
||||||
|
value: this.damage.parts.map(p => p.getFormula(this.actor)).join(' + '),
|
||||||
|
type: this.damage.parts[0].type
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (this.effects.length) {
|
||||||
|
// Apply Active Effects. In Chat Message ?
|
||||||
|
}
|
||||||
|
return this.actor.diceRoll(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const extraDefineSchema = (field, option) => {
|
||||||
|
return {
|
||||||
|
[field]: {
|
||||||
|
// damage: new fields.SchemaField({
|
||||||
|
// parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
|
||||||
|
// }),
|
||||||
|
damage: new DHDamageField(option),
|
||||||
|
roll: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.GENERAL.rollTypes }),
|
||||||
|
trait: new fields.StringField({ nullable: true, initial: null, choices: SYSTEM.ACTOR.abilities }),
|
||||||
|
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
|
||||||
|
}),
|
||||||
|
target: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: SYSTEM.ACTIONS.targetTypes,
|
||||||
|
initial: SYSTEM.ACTIONS.targetTypes.other.id
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
effects: new fields.ArrayField( // ActiveEffect
|
||||||
|
new fields.SchemaField({
|
||||||
|
_id: new fields.DocumentIdField()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}[field]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export class DHAttackAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
...extraDefineSchema('damage', true),
|
||||||
|
...extraDefineSchema('roll'),
|
||||||
|
...extraDefineSchema('target'),
|
||||||
|
...extraDefineSchema('effects')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRollType() {
|
||||||
|
return 'weapon';
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareData() {
|
||||||
|
super.prepareData();
|
||||||
|
if (this.damage.includeBase && !!this.item?.system?.damage) {
|
||||||
|
const baseDamage = this.getParentDamage();
|
||||||
|
this.damage.parts.unshift(new DHDamageData(baseDamage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getParentDamage() {
|
||||||
|
return {
|
||||||
|
multiplier: 'proficiency',
|
||||||
|
dice: this.item?.system?.damage.value,
|
||||||
|
bonus: this.item?.system?.damage.bonus ?? 0,
|
||||||
|
type: this.item?.system?.damage.type,
|
||||||
|
base: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary until full formula parser
|
||||||
|
// getDamageFormula() {
|
||||||
|
// return this.damage.parts.map(p => p.formula).join(' + ');
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHSpellCastAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
...extraDefineSchema('damage'),
|
||||||
|
...extraDefineSchema('roll'),
|
||||||
|
...extraDefineSchema('target'),
|
||||||
|
...extraDefineSchema('effects')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRollType() {
|
||||||
|
return 'spellcast';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHDamageAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
...extraDefineSchema('damage', false),
|
||||||
|
...extraDefineSchema('target'),
|
||||||
|
...extraDefineSchema('effects')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async use(event) {
|
||||||
|
const formula = this.damage.parts.map(p => p.getFormula(this.actor)).join(' + ');
|
||||||
|
if (!formula || formula == '') return;
|
||||||
|
|
||||||
|
let roll = { formula: formula, total: formula };
|
||||||
|
if (isNaN(formula)) {
|
||||||
|
roll = await new Roll(formula).evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const msg = new cls({
|
||||||
|
user: game.user.id,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/damage-roll.hbs',
|
||||||
|
{
|
||||||
|
roll: roll.formula,
|
||||||
|
total: roll.total,
|
||||||
|
type: this.damage.parts.map(p => p.type)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHHealingAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
healing: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.healingTypes,
|
||||||
|
required: true,
|
||||||
|
blank: false,
|
||||||
|
initial: SYSTEM.GENERAL.healingTypes.health.id,
|
||||||
|
label: 'Healing'
|
||||||
|
}),
|
||||||
|
value: new fields.EmbeddedDataField(DHActionDiceData)
|
||||||
|
}),
|
||||||
|
...extraDefineSchema('target'),
|
||||||
|
...extraDefineSchema('effects')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async use(event) {
|
||||||
|
const formula = this.healing.value.getFormula(this.actor);
|
||||||
|
if (!formula || formula == '') return;
|
||||||
|
|
||||||
|
// const roll = await super.use(event);
|
||||||
|
let roll = { formula: formula, total: formula };
|
||||||
|
if (isNaN(formula)) {
|
||||||
|
roll = await new Roll(formula).evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const msg = new cls({
|
||||||
|
user: game.user.id,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/healing-roll.hbs',
|
||||||
|
{
|
||||||
|
roll: roll.formula,
|
||||||
|
total: roll.total,
|
||||||
|
type: this.healing.type
|
||||||
|
}
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
get chatTemplate() {
|
||||||
|
return 'systems/daggerheart/templates/chat/healing-roll.hbs';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHResourceAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
// ...extraDefineSchema('roll'),
|
||||||
|
...extraDefineSchema('target'),
|
||||||
|
...extraDefineSchema('effects'),
|
||||||
|
resource: new fields.SchemaField({
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: [],
|
||||||
|
blank: true,
|
||||||
|
required: false,
|
||||||
|
initial: '',
|
||||||
|
label: 'Resource'
|
||||||
|
}),
|
||||||
|
value: new fields.NumberField({ initial: 0, label: 'Value' })
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHSummonAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
documentUUID: new fields.StringField({ blank: true, initial: '', placeholder: 'Enter a Creature UUID' })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHEffectAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
...extraDefineSchema('effects')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHMacroAction extends DHBaseAction {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
documentUUID: new fields.StringField({ blank: true, initial: '', placeholder: 'Enter a macro UUID' })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async use(event) {
|
||||||
|
const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID,
|
||||||
|
macro = await fromUuid(fixUUID);
|
||||||
|
try {
|
||||||
|
if (!macro) throw new Error(`No macro found for the UUID: ${this.documentUUID}.`);
|
||||||
|
macro.execute();
|
||||||
|
} catch (error) {
|
||||||
|
ui.notifications.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
55
module/data/action/actionDice.mjs
Normal file
55
module/data/action/actionDice.mjs
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import FormulaField from '../fields/formulaField.mjs';
|
||||||
|
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
export class DHActionDiceData extends foundry.abstract.DataModel {
|
||||||
|
/** @override */
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
multiplier: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.multiplierTypes,
|
||||||
|
initial: 'proficiency',
|
||||||
|
label: 'Multiplier'
|
||||||
|
}),
|
||||||
|
dice: new fields.StringField({ choices: SYSTEM.GENERAL.diceTypes, initial: 'd6', label: 'Formula' }),
|
||||||
|
bonus: new fields.NumberField({ nullable: true, initial: null, label: 'Bonus' }),
|
||||||
|
custom: new fields.SchemaField({
|
||||||
|
enabled: new fields.BooleanField({ label: 'Custom Formula' }),
|
||||||
|
formula: new FormulaField({ label: 'Formula' })
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getFormula(actor) {
|
||||||
|
return this.custom.enabled
|
||||||
|
? this.custom.formula
|
||||||
|
: `${actor.system[this.multiplier] ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHDamageField extends fields.SchemaField {
|
||||||
|
constructor(hasBase, options, context = {}) {
|
||||||
|
const damageFields = {
|
||||||
|
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
|
||||||
|
};
|
||||||
|
if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true });
|
||||||
|
super(damageFields, options, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DHDamageData extends DHActionDiceData {
|
||||||
|
/** @override */
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
...super.defineSchema(),
|
||||||
|
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
||||||
|
type: new fields.StringField({
|
||||||
|
choices: SYSTEM.GENERAL.damageTypes,
|
||||||
|
initial: 'physical',
|
||||||
|
label: 'Type',
|
||||||
|
nullable: false,
|
||||||
|
required: true
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,17 +5,19 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
||||||
return {
|
return {
|
||||||
title: new fields.StringField(),
|
title: new fields.StringField(),
|
||||||
origin: new fields.StringField({ required: true }),
|
origin: new fields.StringField({ required: true }),
|
||||||
roll: new fields.StringField({}),
|
dice: new fields.DataField(),
|
||||||
total: new fields.NumberField({ integer: true }),
|
roll: new fields.DataField(),
|
||||||
modifiers: new fields.ArrayField(
|
modifiers: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.NumberField({ integer: true }),
|
value: new fields.NumberField({ integer: true }),
|
||||||
label: new fields.StringField({}),
|
label: new fields.StringField({})
|
||||||
title: new fields.StringField({})
|
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
advantageState: new fields.NumberField({ required: true, choices: [0, 1, 2], initial: 0 }),
|
advantageState: new fields.BooleanField({ nullable: true, initial: null }),
|
||||||
dice: new fields.EmbeddedDataField(DhpAdversaryRollDice),
|
advantage: new fields.SchemaField({
|
||||||
|
dice: new fields.StringField({}),
|
||||||
|
value: new fields.NumberField({ integer: true })
|
||||||
|
}),
|
||||||
targets: new fields.ArrayField(
|
targets: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
id: new fields.StringField({}),
|
id: new fields.StringField({}),
|
||||||
|
|
@ -37,42 +39,8 @@ export default class DHAdversaryRoll extends foundry.abstract.TypeDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
const diceKeys = Object.keys(this.dice.rolls);
|
|
||||||
const highestDiceIndex =
|
|
||||||
diceKeys.length < 2
|
|
||||||
? null
|
|
||||||
: this.dice.rolls[diceKeys[0]].value > this.dice.rolls[diceKeys[1]].value
|
|
||||||
? 0
|
|
||||||
: 1;
|
|
||||||
if (highestDiceIndex !== null) {
|
|
||||||
this.dice.rolls = this.dice.rolls.map((roll, index) => ({
|
|
||||||
...roll,
|
|
||||||
discarded: this.advantageState === 1 ? index !== highestDiceIndex : index === highestDiceIndex
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.targets.forEach(target => {
|
this.targets.forEach(target => {
|
||||||
target.hit = target.difficulty ? this.total >= target.difficulty : this.total >= target.evasion;
|
target.hit = target.difficulty ? this.total >= target.difficulty : this.total >= target.evasion;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DhpAdversaryRollDice extends foundry.abstract.DataModel {
|
|
||||||
static defineSchema() {
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: new fields.StringField({ required: true }),
|
|
||||||
rolls: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
discarded: new fields.BooleanField({ initial: false })
|
|
||||||
})
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get rollTotal() {
|
|
||||||
return this.rolls.reduce((acc, roll) => acc + (!roll.discarded ? roll.value : 0), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,13 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
|
||||||
total: new fields.NumberField({ required: true, integer: true }),
|
total: new fields.NumberField({ required: true, integer: true }),
|
||||||
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false })
|
type: new fields.StringField({ choices: Object.keys(SYSTEM.GENERAL.damageTypes), integer: false })
|
||||||
}),
|
}),
|
||||||
dice: new fields.ArrayField(new fields.EmbeddedDataField(DhpDamageDice)),
|
dice: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
type: new fields.StringField({ required: true }),
|
||||||
|
rolls: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })),
|
||||||
|
total: new fields.NumberField({ integer: true })
|
||||||
|
})
|
||||||
|
),
|
||||||
modifiers: new fields.ArrayField(
|
modifiers: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.NumberField({ required: true, integer: true }),
|
value: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
|
@ -26,18 +32,3 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DhpDamageDice extends foundry.abstract.DataModel {
|
|
||||||
static defineSchema() {
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: new fields.StringField({ required: true }),
|
|
||||||
rolls: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get rollTotal() {
|
|
||||||
return this.rolls.reduce((acc, roll) => acc + roll, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { DualityRollColor } from "../settings/Appearance.mjs";
|
import { DualityRollColor } from '../settings/Appearance.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
const diceField = () =>
|
const diceField = () =>
|
||||||
|
|
@ -18,18 +18,17 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
||||||
return {
|
return {
|
||||||
title: new fields.StringField(),
|
title: new fields.StringField(),
|
||||||
origin: new fields.StringField({ required: true }),
|
origin: new fields.StringField({ required: true }),
|
||||||
roll: new fields.StringField({}),
|
roll: new fields.DataField({}),
|
||||||
modifiers: new fields.ArrayField(
|
modifiers: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
value: new fields.NumberField({ integer: true }),
|
value: new fields.NumberField({ integer: true }),
|
||||||
label: new fields.StringField({}),
|
label: new fields.StringField({})
|
||||||
title: new fields.StringField({})
|
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
hope: diceField(),
|
hope: diceField(),
|
||||||
fear: diceField(),
|
fear: diceField(),
|
||||||
|
advantageState: new fields.BooleanField({ nullable: true, initial: null }),
|
||||||
advantage: diceField(),
|
advantage: diceField(),
|
||||||
disadvantage: diceField(),
|
|
||||||
targets: new fields.ArrayField(
|
targets: new fields.ArrayField(
|
||||||
new fields.SchemaField({
|
new fields.SchemaField({
|
||||||
id: new fields.StringField({}),
|
id: new fields.StringField({}),
|
||||||
|
|
@ -64,15 +63,6 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get total() {
|
|
||||||
const advantage = this.advantage.value
|
|
||||||
? this.advantage.value
|
|
||||||
: this.disadvantage.value
|
|
||||||
? -this.disadvantage.value
|
|
||||||
: 0;
|
|
||||||
return this.diceTotal + advantage + this.modifierTotal.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get diceTotal() {
|
get diceTotal() {
|
||||||
return this.hope.value + this.fear.value;
|
return this.hope.value + this.fear.value;
|
||||||
}
|
}
|
||||||
|
|
@ -112,13 +102,7 @@ export default class DHDualityRoll extends foundry.abstract.TypeDataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareDerivedData() {
|
prepareDerivedData() {
|
||||||
const total = this.total;
|
|
||||||
|
|
||||||
this.hope.discarded = this.hope.value < this.fear.value;
|
this.hope.discarded = this.hope.value < this.fear.value;
|
||||||
this.fear.discarded = this.fear.value < this.hope.value;
|
this.fear.discarded = this.fear.value < this.hope.value;
|
||||||
|
|
||||||
this.targets.forEach(target => {
|
|
||||||
target.hit = target.difficulty ? total >= target.difficulty : total >= target.evasion;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
export { default as FormulaField } from "./formulaField.mjs";
|
export { default as FormulaField } from './formulaField.mjs';
|
||||||
export { default as ForeignDocumentUUIDField } from "./foreignDocumentUUIDField.mjs";
|
export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs';
|
||||||
|
export { default as PseudoDocumentsField } from './pseudoDocumentsField.mjs';
|
||||||
|
|
|
||||||
40
module/data/fields/actionField.mjs
Normal file
40
module/data/fields/actionField.mjs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { actionsTypes } from '../action/_module.mjs';
|
||||||
|
|
||||||
|
// Temporary Solution
|
||||||
|
export default class ActionField extends foundry.data.fields.ObjectField {
|
||||||
|
getModel(value) {
|
||||||
|
return actionsTypes[value.type] ?? actionsTypes.attack;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_cleanType(value, options) {
|
||||||
|
if (!(typeof value === 'object')) value = {};
|
||||||
|
|
||||||
|
const cls = this.getModel(value);
|
||||||
|
if (cls) return cls.cleanData(value, options);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
initialize(value, model, options = {}) {
|
||||||
|
const cls = this.getModel(value);
|
||||||
|
if (cls) return new cls(value, { parent: model, ...options });
|
||||||
|
return foundry.utils.deepClone(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate this field's candidate source data.
|
||||||
|
* @param {object} sourceData Candidate source data of the root model.
|
||||||
|
* @param {any} fieldData The value of this field within the source data.
|
||||||
|
*/
|
||||||
|
migrateSource(sourceData, fieldData) {
|
||||||
|
const cls = this.getModel(fieldData);
|
||||||
|
if (cls) cls.migrateDataSafe(fieldData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,78 +16,78 @@
|
||||||
* Special case StringField which represents a formula.
|
* Special case StringField which represents a formula.
|
||||||
*/
|
*/
|
||||||
export default class FormulaField extends foundry.data.fields.StringField {
|
export default class FormulaField extends foundry.data.fields.StringField {
|
||||||
|
/**
|
||||||
|
* @param {FormulaFieldOptions} [options] - Options which configure the behavior of the field
|
||||||
|
* @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field
|
||||||
|
*/
|
||||||
|
constructor(options, context) {
|
||||||
|
super(options, context);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/** @inheritDoc */
|
||||||
* @param {FormulaFieldOptions} [options] - Options which configure the behavior of the field
|
static get _defaults() {
|
||||||
* @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field
|
return foundry.utils.mergeObject(super._defaults, {
|
||||||
*/
|
deterministic: false
|
||||||
constructor(options, context) {
|
});
|
||||||
super(options, context);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** @inheritDoc */
|
/* -------------------------------------------- */
|
||||||
static get _defaults() {
|
|
||||||
return foundry.utils.mergeObject(super._defaults, {
|
|
||||||
deterministic: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/** @inheritDoc */
|
||||||
|
_validateType(value) {
|
||||||
|
const roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, '1'));
|
||||||
|
roll.evaluateSync({ strict: false });
|
||||||
|
if (this.options.deterministic && !roll.isDeterministic)
|
||||||
|
throw new Error(`must not contain dice terms: ${value}`);
|
||||||
|
super._validateType(value);
|
||||||
|
}
|
||||||
|
|
||||||
/** @inheritDoc */
|
/* -------------------------------------------- */
|
||||||
_validateType(value) {
|
/* Active Effect Integration */
|
||||||
const roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, "1"));
|
/* -------------------------------------------- */
|
||||||
roll.evaluateSync({ strict: false });
|
|
||||||
if (this.options.deterministic && !roll.isDeterministic) throw new Error(`must not contain dice terms: ${value}`);
|
|
||||||
super._validateType(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/** @override */
|
||||||
/* Active Effect Integration */
|
_castChangeDelta(delta) {
|
||||||
/* -------------------------------------------- */
|
return this._cast(delta).trim();
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/* -------------------------------------------- */
|
||||||
_castChangeDelta(delta) {
|
|
||||||
return this._cast(delta).trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/** @override */
|
||||||
|
_applyChangeAdd(value, delta, model, change) {
|
||||||
|
if (!value) return delta;
|
||||||
|
const operator = delta.startsWith('-') ? '-' : '+';
|
||||||
|
delta = delta.replace(/^[+-]/, '').trim();
|
||||||
|
return `${value} ${operator} ${delta}`;
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/* -------------------------------------------- */
|
||||||
_applyChangeAdd(value, delta, model, change) {
|
|
||||||
if (!value) return delta;
|
|
||||||
const operator = delta.startsWith("-") ? "-" : "+";
|
|
||||||
delta = delta.replace(/^[+-]/, "").trim();
|
|
||||||
return `${value} ${operator} ${delta}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/** @override */
|
||||||
|
_applyChangeMultiply(value, delta, model, change) {
|
||||||
|
if (!value) return delta;
|
||||||
|
const terms = new Roll(value).terms;
|
||||||
|
if (terms.length > 1) return `(${value}) * ${delta}`;
|
||||||
|
return `${value} * ${delta}`;
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/* -------------------------------------------- */
|
||||||
_applyChangeMultiply(value, delta, model, change) {
|
|
||||||
if (!value) return delta;
|
|
||||||
const terms = new Roll(value).terms;
|
|
||||||
if (terms.length > 1) return `(${value}) * ${delta}`;
|
|
||||||
return `${value} * ${delta}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/** @override */
|
||||||
|
_applyChangeUpgrade(value, delta, model, change) {
|
||||||
|
if (!value) return delta;
|
||||||
|
const terms = new Roll(value).terms;
|
||||||
|
if (terms.length === 1 && terms[0].fn === 'max') return value.replace(/\)$/, `, ${delta})`);
|
||||||
|
return `max(${value}, ${delta})`;
|
||||||
|
}
|
||||||
|
|
||||||
/** @override */
|
/* -------------------------------------------- */
|
||||||
_applyChangeUpgrade(value, delta, model, change) {
|
|
||||||
if (!value) return delta;
|
|
||||||
const terms = new Roll(value).terms;
|
|
||||||
if ((terms.length === 1) && (terms[0].fn === "max")) return value.replace(/\)$/, `, ${delta})`);
|
|
||||||
return `max(${value}, ${delta})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/** @override */
|
||||||
|
_applyChangeDowngrade(value, delta, model, change) {
|
||||||
/** @override */
|
if (!value) return delta;
|
||||||
_applyChangeDowngrade(value, delta, model, change) {
|
const terms = new Roll(value).terms;
|
||||||
if (!value) return delta;
|
if (terms.length === 1 && terms[0].fn === 'min') return value.replace(/\)$/, `, ${delta})`);
|
||||||
const terms = new Roll(value).terms;
|
return `min(${value}, ${delta})`;
|
||||||
if ((terms.length === 1) && (terms[0].fn === "min")) return value.replace(/\)$/, `, ${delta})`);
|
}
|
||||||
return `min(${value}, ${delta})`;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
56
module/data/fields/pseudoDocumentsField.mjs
Normal file
56
module/data/fields/pseudoDocumentsField.mjs
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import PseudoDocument from '../pseudo-documents/base/pseudoDocument.mjs';
|
||||||
|
|
||||||
|
const { TypedObjectField, TypedSchemaField } = foundry.data.fields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef _PseudoDocumentsFieldOptions
|
||||||
|
* @property {Number} [max] - The maximum amount of elements (default: `Infinity`)
|
||||||
|
* @property {String[]} [validTypes] - Allowed pseudo-documents types (default: `[]`)
|
||||||
|
* @property {Function} [validateKey] - callback for validate keys of the object;
|
||||||
|
|
||||||
|
* @typedef {foundry.data.types.DataFieldOptions & _PseudoDocumentsFieldOptions} PseudoDocumentsFieldOptions
|
||||||
|
*/
|
||||||
|
export default class PseudoDocumentsField extends TypedObjectField {
|
||||||
|
/**
|
||||||
|
* @param {PseudoDocument} model - The PseudoDocument of each entry in this collection.
|
||||||
|
* @param {PseudoDocumentsFieldOptions} [options] - Options which configure the behavior of the field
|
||||||
|
* @param {foundry.data.types.DataFieldContext} [context] - Additional context which describes the field
|
||||||
|
*/
|
||||||
|
constructor(model, options = {}, context = {}) {
|
||||||
|
options.validateKey ||= key => foundry.data.validators.isValidId(key);
|
||||||
|
if (!foundry.utils.isSubclass(model, PseudoDocument)) throw new Error('The model must be a PseudoDocument');
|
||||||
|
|
||||||
|
const allTypes = model.TYPES;
|
||||||
|
|
||||||
|
const filteredTypes = options.validTypes
|
||||||
|
? Object.fromEntries(
|
||||||
|
Object.entries(allTypes).filter(([key]) => options.validTypes.includes(key))
|
||||||
|
)
|
||||||
|
: allTypes;
|
||||||
|
|
||||||
|
const field = new TypedSchemaField(filteredTypes);
|
||||||
|
super(field, options, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
static get _defaults() {
|
||||||
|
return Object.assign(super._defaults, {
|
||||||
|
max: Infinity,
|
||||||
|
validTypes: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
_validateType(value, options = {}) {
|
||||||
|
if (Object.keys(value).length > this.max) throw new Error(`cannot have more than ${this.max} elements`);
|
||||||
|
return super._validateType(value, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
initialize(value, model, options = {}) {
|
||||||
|
if (!value) return;
|
||||||
|
value = super.initialize(value, model, options);
|
||||||
|
const collection = new foundry.utils.Collection(Object.values(value).map(d => [d._id, d]));
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,27 +10,27 @@ import DHSubclass from './subclass.mjs';
|
||||||
import DHWeapon from './weapon.mjs';
|
import DHWeapon from './weapon.mjs';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
DHAncestry,
|
DHAncestry,
|
||||||
DHArmor,
|
DHArmor,
|
||||||
DHClass,
|
DHClass,
|
||||||
DHCommunity,
|
DHCommunity,
|
||||||
DHConsumable,
|
DHConsumable,
|
||||||
DHDomainCard,
|
DHDomainCard,
|
||||||
DHFeature,
|
DHFeature,
|
||||||
DHMiscellaneous,
|
DHMiscellaneous,
|
||||||
DHSubclass,
|
DHSubclass,
|
||||||
DHWeapon,
|
DHWeapon
|
||||||
}
|
};
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
ancestry: DHAncestry,
|
ancestry: DHAncestry,
|
||||||
armor: DHArmor,
|
armor: DHArmor,
|
||||||
class: DHClass,
|
class: DHClass,
|
||||||
community: DHCommunity,
|
community: DHCommunity,
|
||||||
consumable: DHConsumable,
|
consumable: DHConsumable,
|
||||||
domainCard: DHDomainCard,
|
domainCard: DHDomainCard,
|
||||||
feature: DHFeature,
|
feature: DHFeature,
|
||||||
miscellaneous: DHMiscellaneous,
|
miscellaneous: DHMiscellaneous,
|
||||||
subclass: DHSubclass,
|
subclass: DHSubclass,
|
||||||
weapon: DHWeapon,
|
weapon: DHWeapon
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
* @property {string} type - The system type that this data model represents.
|
* @property {string} type - The system type that this data model represents.
|
||||||
* @property {boolean} hasDescription - Indicates whether items of this type have description field
|
* @property {boolean} hasDescription - Indicates whether items of this type have description field
|
||||||
* @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field
|
* @property {boolean} isQuantifiable - Indicates whether items of this type have quantity field
|
||||||
|
* @property {Record<string,string>} embedded - Record of document names of pseudo-documents and the path to the collection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
@ -16,7 +17,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
|
||||||
label: "Base Item",
|
label: "Base Item",
|
||||||
type: "base",
|
type: "base",
|
||||||
hasDescription: false,
|
hasDescription: false,
|
||||||
isQuantifiable: false
|
isQuantifiable: false,
|
||||||
|
embedded: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import DaggerheartAction from '../action.mjs';
|
import DHAction from '../action/action.mjs';
|
||||||
import BaseDataItem from './base.mjs';
|
import BaseDataItem from './base.mjs';
|
||||||
|
|
||||||
export default class DHDomainCard extends BaseDataItem {
|
export default class DHDomainCard extends BaseDataItem {
|
||||||
|
|
@ -22,7 +22,7 @@ export default class DHDomainCard extends BaseDataItem {
|
||||||
type: new fields.StringField({ choices: SYSTEM.DOMAIN.cardTypes, required: true, blank: true }),
|
type: new fields.StringField({ choices: SYSTEM.DOMAIN.cardTypes, required: true, blank: true }),
|
||||||
foundation: new fields.BooleanField({ initial: false }),
|
foundation: new fields.BooleanField({ initial: false }),
|
||||||
inVault: new fields.BooleanField({ initial: false }),
|
inVault: new fields.BooleanField({ initial: false }),
|
||||||
actions: new fields.ArrayField(new fields.EmbeddedDataField(DaggerheartAction))
|
actions: new fields.ArrayField(new fields.EmbeddedDataField(DHAction))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import { getTier } from '../../helpers/utils.mjs';
|
import { getTier } from '../../helpers/utils.mjs';
|
||||||
import DaggerheartAction from '../action.mjs';
|
import DHAction from '../action/action.mjs';
|
||||||
import BaseDataItem from './base.mjs';
|
import BaseDataItem from './base.mjs';
|
||||||
|
|
||||||
export default class DHFeature extends BaseDataItem {
|
export default class DHFeature extends BaseDataItem {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static get metadata() {
|
static get metadata() {
|
||||||
return foundry.utils.mergeObject(super.metadata, {
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
label: "TYPES.Item.feature",
|
label: 'TYPES.Item.feature',
|
||||||
type: "feature",
|
type: 'feature',
|
||||||
hasDescription: true,
|
hasDescription: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,7 +17,7 @@ export default class DHFeature extends BaseDataItem {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
...super.defineSchema(),
|
...super.defineSchema(),
|
||||||
|
|
||||||
//A type of feature seems unnecessary
|
//A type of feature seems unnecessary
|
||||||
type: new fields.StringField({ choices: SYSTEM.ITEM.featureTypes }),
|
type: new fields.StringField({ choices: SYSTEM.ITEM.featureTypes }),
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ export default class DHFeature extends BaseDataItem {
|
||||||
choices: SYSTEM.ITEM.actionTypes,
|
choices: SYSTEM.ITEM.actionTypes,
|
||||||
initial: SYSTEM.ITEM.actionTypes.passive.id
|
initial: SYSTEM.ITEM.actionTypes.passive.id
|
||||||
}),
|
}),
|
||||||
//TODO: remove featureType field
|
//TODO: remove featureType field
|
||||||
featureType: new fields.SchemaField({
|
featureType: new fields.SchemaField({
|
||||||
type: new fields.StringField({
|
type: new fields.StringField({
|
||||||
choices: SYSTEM.ITEM.valueTypes,
|
choices: SYSTEM.ITEM.valueTypes,
|
||||||
|
|
@ -75,9 +75,10 @@ export default class DHFeature extends BaseDataItem {
|
||||||
{ nullable: true, initial: null }
|
{ nullable: true, initial: null }
|
||||||
),
|
),
|
||||||
dataField: new fields.StringField({}),
|
dataField: new fields.StringField({}),
|
||||||
appliesOn: new fields.StringField({
|
appliesOn: new fields.StringField(
|
||||||
choices: SYSTEM.EFFECTS.applyLocations,
|
{
|
||||||
},
|
choices: SYSTEM.EFFECTS.applyLocations
|
||||||
|
},
|
||||||
{ nullable: true, initial: null }
|
{ nullable: true, initial: null }
|
||||||
),
|
),
|
||||||
applyLocationChoices: new fields.TypedObjectField(new fields.StringField({}), {
|
applyLocationChoices: new fields.TypedObjectField(new fields.StringField({}), {
|
||||||
|
|
@ -92,7 +93,7 @@ export default class DHFeature extends BaseDataItem {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
actions: new fields.ArrayField(new fields.EmbeddedDataField(DaggerheartAction))
|
actions: new fields.ArrayField(new fields.EmbeddedDataField(DHAction))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
import BaseDataItem from "./base.mjs";
|
import BaseDataItem from './base.mjs';
|
||||||
import FormulaField from "../fields/formulaField.mjs";
|
import FormulaField from '../fields/formulaField.mjs';
|
||||||
|
import PseudoDocumentsField from '../fields/pseudoDocumentsField.mjs';
|
||||||
|
import BaseFeatureData from '../pseudo-documents/feature/baseFeatureData.mjs';
|
||||||
|
import ActionField from '../fields/actionField.mjs';
|
||||||
|
|
||||||
export default class DHWeapon extends BaseDataItem {
|
export default class DHWeapon extends BaseDataItem {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static get metadata() {
|
static get metadata() {
|
||||||
return foundry.utils.mergeObject(super.metadata, {
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
label: "TYPES.Item.weapon",
|
label: 'TYPES.Item.weapon',
|
||||||
type: "weapon",
|
type: 'weapon',
|
||||||
hasDescription: true,
|
hasDescription: true,
|
||||||
isQuantifiable: true,
|
isQuantifiable: true,
|
||||||
|
embedded: {
|
||||||
|
feature: 'featureTest'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,8 +39,15 @@ export default class DHWeapon extends BaseDataItem {
|
||||||
initial: 'physical'
|
initial: 'physical'
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
||||||
feature: new fields.StringField({ choices: SYSTEM.ITEM.weaponFeatures, blank: true }),
|
feature: new fields.StringField({ choices: SYSTEM.ITEM.weaponFeatures, blank: true }),
|
||||||
|
featureTest: new PseudoDocumentsField(BaseFeatureData, {
|
||||||
|
required: true,
|
||||||
|
nullable: true,
|
||||||
|
max: 1,
|
||||||
|
validTypes: ['weapon']
|
||||||
|
}),
|
||||||
|
// actions: new fields.ArrayField(new fields.EmbeddedDataField(DHAttackAction))
|
||||||
|
actions: new fields.ArrayField(new ActionField())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
module/data/pseudo-documents/_module.mjs
Normal file
2
module/data/pseudo-documents/_module.mjs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as base } from './base/pseudoDocument.mjs';
|
||||||
|
export * as feature from './feature/_module.mjs';
|
||||||
213
module/data/pseudo-documents/base/base.mjs
Normal file
213
module/data/pseudo-documents/base/base.mjs
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
/**
|
||||||
|
* @typedef {object} PseudoDocumentMetadata
|
||||||
|
* @property {string} name - The document name of this pseudo-document
|
||||||
|
* @property {Record<string, string>} embedded - Record of document names and their collection paths
|
||||||
|
* @property {typeof foundry.applications.api.ApplicationV2} [sheetClass] - The class used to render this pseudo-document
|
||||||
|
* @property {string} defaultArtwork - The default image used for newly created documents
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Base class for pseudo-documents
|
||||||
|
* @extends {foundry.abstract.DataModel}
|
||||||
|
*/
|
||||||
|
export default class BasePseudoDocument extends foundry.abstract.DataModel {
|
||||||
|
/**
|
||||||
|
* Pseudo-document metadata.
|
||||||
|
* @returns {PseudoDocumentMetadata}
|
||||||
|
*/
|
||||||
|
static get metadata() {
|
||||||
|
return {
|
||||||
|
name: '',
|
||||||
|
embedded: {},
|
||||||
|
defaultArtwork: foundry.documents.Item.DEFAULT_ICON,
|
||||||
|
sheetClass: CONFIG.daggerheart.pseudoDocuments.sheetClass,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static LOCALIZATION_PREFIXES = ['DOCUMENT'];
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
static defineSchema() {
|
||||||
|
const { fields } = foundry.data;
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: new fields.DocumentIdField({ initial: () => foundry.utils.randomID() }),
|
||||||
|
name: new fields.StringField({ required: true, blank: false, textSearch: true }),
|
||||||
|
img: new fields.FilePathField({ categories: ['IMAGE'], initial: this.metadata.defaultArtwork }),
|
||||||
|
description: new fields.HTMLField({ textSearch: true })
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Instance Properties */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of this pseudo-document.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get id() {
|
||||||
|
return this._id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The uuid of this document.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get uuid() {
|
||||||
|
let parent = this.parent;
|
||||||
|
while (!(parent instanceof BasePseudoDocument) && !(parent instanceof foundry.abstract.Document))
|
||||||
|
parent = parent.parent;
|
||||||
|
return [parent.uuid, this.constructor.metadata.name, this.id].join('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parent document of this pseudo-document.
|
||||||
|
* @type {foundry.abstract.Document}
|
||||||
|
*/
|
||||||
|
get document() {
|
||||||
|
let parent = this;
|
||||||
|
while (!(parent instanceof foundry.abstract.Document)) parent = parent.parent;
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Item to which this PseudoDocument belongs, if applicable.
|
||||||
|
* @type {foundry.documents.Item|null}
|
||||||
|
*/
|
||||||
|
get item() {
|
||||||
|
return this.parent?.parent instanceof Item ? this.parent.parent : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actor to which this PseudoDocument's item belongs, if the item is embedded.
|
||||||
|
* @type {foundry.documents.Actor|null}
|
||||||
|
*/
|
||||||
|
get actor() {
|
||||||
|
return this.item?.parent ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The property path to this pseudo document relative to its parent document.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
get fieldPath() {
|
||||||
|
const fp = this.schema.fieldPath;
|
||||||
|
let path = fp.slice(0, fp.lastIndexOf('element') - 1);
|
||||||
|
|
||||||
|
if (this.parent instanceof BasePseudoDocument) {
|
||||||
|
path = [this.parent.fieldPath, this.parent.id, path].join('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Embedded Document Methods */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an embedded pseudo-document.
|
||||||
|
* @param {string} embeddedName The document name of the embedded pseudo-document.
|
||||||
|
* @param {string} id The id of the embedded pseudo-document.
|
||||||
|
* @param {object} [options] Retrieval options.
|
||||||
|
* @param {boolean} [options.strinct] Throw an error if the embedded pseudo-document does not exist?
|
||||||
|
* @returns {PseudoDocument|null}
|
||||||
|
*/
|
||||||
|
getEmbeddedDocument(embeddedName, id, { strict = false } = {}) {
|
||||||
|
const embeds = this.constructor.metadata.embedded ?? {};
|
||||||
|
if (embeddedName in embeds) {
|
||||||
|
return foundry.utils.getProperty(this, embeds[embeddedName]).get(id, { strict }) ?? null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* CRUD Operations */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this pseudo-document exist in the document's source?
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
get isSource() {
|
||||||
|
const source = foundry.utils.getProperty(this.document._source, this.fieldPath);
|
||||||
|
if (foundry.utils.getType(source) !== 'Object') {
|
||||||
|
throw new Error('Source is not an object!');
|
||||||
|
}
|
||||||
|
return this.id in source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance of this pseudo-document.
|
||||||
|
* @param {object} [data] The data used for the creation.
|
||||||
|
* @param {object} operation The context of the update operation.
|
||||||
|
* @param {foundry.abstract.Document} operation.parent The parent of this document.
|
||||||
|
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||||
|
*/
|
||||||
|
static async create(data = {}, { parent, ...operation } = {}) {
|
||||||
|
if (!parent) {
|
||||||
|
throw new Error('A parent document must be specified for the creation of a pseudo-document!');
|
||||||
|
}
|
||||||
|
const id =
|
||||||
|
operation.keepId && foundry.data.validators.isValidId(data._id) ? data._id : foundry.utils.randomID();
|
||||||
|
|
||||||
|
const fieldPath = parent.system.constructor.metadata.embedded?.[this.metadata.name];
|
||||||
|
if (!fieldPath) {
|
||||||
|
throw new Error(
|
||||||
|
`A ${parent.documentName} of type '${parent.type}' does not support ${this.metadata.name}!`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const update = { [`system.${fieldPath}.${id}`]: { ...data, _id: id } };
|
||||||
|
const updatedParent = await parent.update(update, operation);
|
||||||
|
return foundry.utils.getProperty(updatedParent, `system.${fieldPath}.${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete this pseudo-document.
|
||||||
|
* @param {object} [operation] The context of the operation.
|
||||||
|
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||||
|
*/
|
||||||
|
async delete(operation = {}) {
|
||||||
|
if (!this.isSource) throw new Error('You cannot delete a non-source pseudo-document!');
|
||||||
|
const update = { [`${this.fieldPath}.-=${this.id}`]: null };
|
||||||
|
return this.document.update(update, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duplicate this pseudo-document.
|
||||||
|
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||||
|
*/
|
||||||
|
async duplicate() {
|
||||||
|
if (!this.isSource) throw new Error('You cannot duplicate a non-source pseudo-document!');
|
||||||
|
const docData = foundry.utils.mergeObject(this.toObject(), {
|
||||||
|
name: game.i18n.format('DOCUMENT.CopyOf', { name: this.name })
|
||||||
|
});
|
||||||
|
return this.constructor.create(docData, { parent: this.document });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update this pseudo-document.
|
||||||
|
* @param {object} [change] The change to perform.
|
||||||
|
* @param {object} [operation] The context of the operation.
|
||||||
|
* @returns {Promise<foundry.abstract.Document>} A promise that resolves to the updated document.
|
||||||
|
*/
|
||||||
|
async update(change = {}, operation = {}) {
|
||||||
|
if (!this.isSource) throw new Error('You cannot update a non-source pseudo-document!');
|
||||||
|
const path = [this.fieldPath, this.id].join('.');
|
||||||
|
const update = { [path]: change };
|
||||||
|
return this.document.update(update, operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
59
module/data/pseudo-documents/base/pseudoDocument.mjs
Normal file
59
module/data/pseudo-documents/base/pseudoDocument.mjs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import BasePseudoDocument from './base.mjs';
|
||||||
|
import SheetManagementMixin from './sheetManagementMixin.mjs';
|
||||||
|
|
||||||
|
/** @extends BasePseudoDocument */
|
||||||
|
export default class PseudoDocument extends SheetManagementMixin(BasePseudoDocument) {
|
||||||
|
static get TYPES() {
|
||||||
|
const { types } = CONFIG.daggerheart.pseudoDocuments[this.metadata.name];
|
||||||
|
const typeEntries = Object.entries(types).map(([key, { documentClass }]) => [key, documentClass]);
|
||||||
|
return (this._TYPES ??= Object.freeze(Object.fromEntries(typeEntries)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static _TYPES;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this shape.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static TYPE = '';
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
static getTypesChoices(validTypes) {
|
||||||
|
const { types } = CONFIG.daggerheart.pseudoDocuments[model.metadata.name];
|
||||||
|
const typeEntries = Object.entries(types)
|
||||||
|
.map(([key, { label }]) => [key, label])
|
||||||
|
.filter(([key]) => !validTypes || validTypes.includes(key));
|
||||||
|
|
||||||
|
return Object.entries(typeEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/** @override */
|
||||||
|
static defineSchema() {
|
||||||
|
const { fields } = foundry.data;
|
||||||
|
|
||||||
|
return Object.assign(super.defineSchema(), {
|
||||||
|
type: new fields.StringField({
|
||||||
|
required: true,
|
||||||
|
blank: false,
|
||||||
|
initial: this.TYPE,
|
||||||
|
validate: value => value === this.TYPE,
|
||||||
|
validationError: `must be equal to "${this.TYPE}"`
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritdoc */
|
||||||
|
static async create(data = {}, { parent, ...operation } = {}) {
|
||||||
|
data = foundry.utils.deepClone(data);
|
||||||
|
if (!data.type) data.type = Object.keys(this.TYPES)[0];
|
||||||
|
if (!(data.type in this.TYPES)) {
|
||||||
|
throw new Error(
|
||||||
|
`The '${data.type}' type is not a valid type for a '${this.metadata.documentName}' pseudo-document!`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return super.create(data, { parent, ...operation });
|
||||||
|
}
|
||||||
|
}
|
||||||
158
module/data/pseudo-documents/base/sheetManagementMixin.mjs
Normal file
158
module/data/pseudo-documents/base/sheetManagementMixin.mjs
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
import BasePseudoDocument from './base.mjs';
|
||||||
|
const { ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mixin that adds sheet management capabilities to pseudo-documents
|
||||||
|
* @template {typeof BasePseudoDocument} T
|
||||||
|
* @param {T} Base
|
||||||
|
* @returns {T & typeof PseudoDocumentWithSheets}
|
||||||
|
*/
|
||||||
|
export default function SheetManagementMixin(Base) {
|
||||||
|
class PseudoDocumentWithSheets extends Base {
|
||||||
|
/**
|
||||||
|
* Reference to the sheet of this pseudo-document.
|
||||||
|
* @type {ApplicationV2|null}
|
||||||
|
*/
|
||||||
|
get sheet() {
|
||||||
|
if (this._sheet) return this._sheet;
|
||||||
|
const cls = this.constructor.metadata.sheetClass ?? ApplicationV2;
|
||||||
|
|
||||||
|
if (!foundry.utils.isSubclass(cls, ApplicationV2)) {
|
||||||
|
return void ui.notifications.error(
|
||||||
|
'Daggerheart | Error on PseudoDocument | sheetClass must be ApplicationV2'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sheet = new cls({ document: this });
|
||||||
|
this._sheet = sheet;
|
||||||
|
return sheet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Static Properties */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of apps what should be re-render.
|
||||||
|
* @type {Set<ApplicationV2>}
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
_apps = new Set();
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Existing sheets of a specific type for a specific document.
|
||||||
|
* @type {ApplicationV2 | null}
|
||||||
|
*/
|
||||||
|
_sheet = null;
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Display Methods */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render all the Application instances which are connected to this PseudoDocument.
|
||||||
|
* @param {ApplicationRenderOptions} [options] Rendering options.
|
||||||
|
*/
|
||||||
|
render(options) {
|
||||||
|
for (const app of this._apps ?? []) {
|
||||||
|
app.render({ window: { title: app.title }, ...options });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an application to respond to updates to a certain document.
|
||||||
|
* @param {ApplicationV2} app Application to update.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
_registerApp(app) {
|
||||||
|
this._apps.add(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an application from the render registry.
|
||||||
|
* @param {ApplicationV2} app Application to stop watching.
|
||||||
|
*/
|
||||||
|
_unregisterApp(app) {
|
||||||
|
this._apps.delete(app);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Drag and Drop */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize salient information for this PseudoDocument when dragging it.
|
||||||
|
* @returns {object} An object of drag data.
|
||||||
|
*/
|
||||||
|
toDragData() {
|
||||||
|
const dragData = { type: this.documentName, data: this.toObject() };
|
||||||
|
if (this.id) dragData.uuid = this.uuid;
|
||||||
|
return dragData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
/* Dialog Methods */
|
||||||
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn a dialog for creating a new PseudoDocument.
|
||||||
|
* @param {object} [data] Data to pre-populate the document with.
|
||||||
|
* @param {object} context
|
||||||
|
* @param {foundry.documents.Item} context.parent A parent for the document.
|
||||||
|
* @param {string[]|null} [context.types] A list of types to restrict the choices to, or null for no restriction.
|
||||||
|
* @returns {Promise<BasePseudoDocument|null>}
|
||||||
|
*/
|
||||||
|
static async createDialog(data = {}, { parent, types = null, ...options } = {}) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Present a Dialog form to confirm deletion of this PseudoDocument.
|
||||||
|
* @param {object} [options] - Additional options passed to `DialogV2.confirm`;
|
||||||
|
* @returns {Promise<foundry.abstract.Document>} A Promise which resolves to the deleted PseudoDocument.
|
||||||
|
*/
|
||||||
|
async deleteDialog(options = {}) {
|
||||||
|
const type = game.i18n.localize(this.constructor.metadata.label);
|
||||||
|
const content = options.content ?? `<p>
|
||||||
|
<strong>${game.i18n.localize("AreYouSure")}</strong>
|
||||||
|
${game.i18n.format("SIDEBAR.DeleteWarning", { type })}
|
||||||
|
</p>`;
|
||||||
|
|
||||||
|
return foundry.applications.api.DialogV2.confirm({
|
||||||
|
content,
|
||||||
|
yes: { callback: () => this.delete(operation) },
|
||||||
|
window: {
|
||||||
|
icon: "fa-solid fa-trash",
|
||||||
|
title: `${game.i18n.format("DOCUMENT.Delete", { type })}: ${this.name}`
|
||||||
|
},
|
||||||
|
...options
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the default new name for a Document
|
||||||
|
* @param {object} collection - Collection of Documents
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
static defaultName(collection) {
|
||||||
|
const documentName = this.metadata.name;
|
||||||
|
const takenNames = new Set();
|
||||||
|
for (const document of collection) takenNames.add(document.name);
|
||||||
|
|
||||||
|
const config = CONFIG.daggerheart.pseudoDocuments[documentName];
|
||||||
|
const baseName = game.i18n.localize(config.label);
|
||||||
|
let name = baseName;
|
||||||
|
let index = 1;
|
||||||
|
while (takenNames.has(name)) name = `${baseName} (${++index})`;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PseudoDocumentWithSheets;
|
||||||
|
}
|
||||||
2
module/data/pseudo-documents/feature/_module.mjs
Normal file
2
module/data/pseudo-documents/feature/_module.mjs
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export { default as BaseFeatureData } from './baseFeatureData.mjs';
|
||||||
|
export { default as WeaponFeature } from './weaponFeature.mjs';
|
||||||
24
module/data/pseudo-documents/feature/baseFeatureData.mjs
Normal file
24
module/data/pseudo-documents/feature/baseFeatureData.mjs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
import PseudoDocument from '../base/pseudoDocument.mjs';
|
||||||
|
|
||||||
|
export default class BaseFeatureData extends PseudoDocument {
|
||||||
|
/**@inheritdoc */
|
||||||
|
static get metadata() {
|
||||||
|
return foundry.utils.mergeObject(
|
||||||
|
super.metadata,
|
||||||
|
{
|
||||||
|
name: 'feature',
|
||||||
|
embedded: {},
|
||||||
|
//sheetClass: null //TODO: define feature-sheet
|
||||||
|
},
|
||||||
|
{ inplace: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static defineSchema() {
|
||||||
|
const { fields } = foundry.data;
|
||||||
|
const schema = super.defineSchema();
|
||||||
|
return Object.assign(schema, {
|
||||||
|
subtype: new fields.StringField({ initial: 'test' })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
6
module/data/pseudo-documents/feature/weaponFeature.mjs
Normal file
6
module/data/pseudo-documents/feature/weaponFeature.mjs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import BaseFeatureData from './baseFeatureData.mjs';
|
||||||
|
|
||||||
|
export default class WeaponFeature extends BaseFeatureData {
|
||||||
|
/**@override */
|
||||||
|
static TYPE = 'weapon';
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import NpcRollSelectionDialog from '../applications/npcRollSelectionDialog.mjs';
|
||||||
import RollSelectionDialog from '../applications/rollSelectionDialog.mjs';
|
import RollSelectionDialog from '../applications/rollSelectionDialog.mjs';
|
||||||
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
import { GMUpdateEvent, socketEvent } from '../helpers/socket.mjs';
|
||||||
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
|
||||||
|
import DHDualityRoll from '../data/chat-message/dualityRoll.mjs';
|
||||||
|
|
||||||
export default class DhpActor extends Actor {
|
export default class DhpActor extends Actor {
|
||||||
async _preCreate(data, options, user) {
|
async _preCreate(data, options, user) {
|
||||||
|
|
@ -246,87 +247,52 @@ export default class DhpActor extends Actor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async diceRoll(modifier, shiftKey) {
|
/**
|
||||||
if (this.type === 'character') {
|
* @param {object} config
|
||||||
return await this.dualityRoll(modifier, shiftKey);
|
* @param {Event} config.event
|
||||||
} else {
|
* @param {string} config.title
|
||||||
return await this.npcRoll(modifier, shiftKey);
|
* @param {object} config.roll
|
||||||
}
|
* @param {number} config.roll.modifier
|
||||||
}
|
* @param {boolean} [config.roll.simple=false]
|
||||||
|
* @param {string} [config.roll.type]
|
||||||
async npcRoll(modifier, shiftKey) {
|
* @param {number} [config.roll.difficulty]
|
||||||
let advantage = null;
|
* @param {any} [config.damage]
|
||||||
|
* @param {object} [config.chatMessage]
|
||||||
const modifiers = [
|
* @param {string} config.chatMessage.template
|
||||||
{
|
* @param {boolean} [config.chatMessage.mute]
|
||||||
value: Number.parseInt(modifier.value),
|
* @param {boolean} [config.checkTarget]
|
||||||
label: modifier.value >= 0 ? `+${modifier.value}` : `-${modifier.value}`,
|
*/
|
||||||
title: modifier.title
|
async diceRoll(config) {
|
||||||
}
|
|
||||||
];
|
|
||||||
if (!shiftKey) {
|
|
||||||
const dialogClosed = new Promise((resolve, _) => {
|
|
||||||
new NpcRollSelectionDialog(this.system.experiences, resolve).render(true);
|
|
||||||
});
|
|
||||||
const result = await dialogClosed;
|
|
||||||
|
|
||||||
advantage = result.advantage;
|
|
||||||
result.experiences.forEach(x =>
|
|
||||||
modifiers.push({
|
|
||||||
value: x.value,
|
|
||||||
label: x.value >= 0 ? `+${x.value}` : `-${x.value}`,
|
|
||||||
title: x.description
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const roll = Roll.create(
|
|
||||||
`${advantage === true || advantage === false ? 2 : 1}d20${advantage === true ? 'kh' : advantage === false ? 'kl' : ''} ${modifiers.map(x => `+ ${x.value}`).join(' ')}`
|
|
||||||
);
|
|
||||||
let rollResult = await roll.evaluate();
|
|
||||||
const dice = [];
|
|
||||||
for (var i = 0; i < rollResult.terms.length; i++) {
|
|
||||||
const term = rollResult.terms[i];
|
|
||||||
if (term.faces) {
|
|
||||||
dice.push({ type: `d${term.faces}`, rolls: term.results.map(x => ({ value: x.result })) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is Only ever one dice term here
|
|
||||||
return { roll, dice: dice[0], modifiers, advantageState: advantage === true ? 1 : advantage === false ? 2 : 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
async dualityRoll(modifier, shiftKey) {
|
|
||||||
let hopeDice = 'd12',
|
let hopeDice = 'd12',
|
||||||
fearDice = 'd12',
|
fearDice = 'd12',
|
||||||
advantageDice = null,
|
advantageDice = 'd6',
|
||||||
disadvantageDice = null;
|
disadvantageDice = 'd6',
|
||||||
|
advantage = config.event.altKey ? true : config.event.ctrlKey ? false : null,
|
||||||
|
targets,
|
||||||
|
damage = config.damage,
|
||||||
|
modifiers = this.formatRollModifier(config.roll),
|
||||||
|
rollConfig,
|
||||||
|
formula,
|
||||||
|
hope,
|
||||||
|
fear;
|
||||||
|
|
||||||
const modifiers =
|
if (!config.event.shiftKey && !config.event.altKey && !config.event.ctrlKey) {
|
||||||
modifier.value !== null
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
value: modifier.value ? Number.parseInt(modifier.value) : 0,
|
|
||||||
label:
|
|
||||||
modifier.value >= 0
|
|
||||||
? `${modifier.title} +${modifier.value}`
|
|
||||||
: `${modifier.title} ${modifier.value}`,
|
|
||||||
title: modifier.title
|
|
||||||
}
|
|
||||||
]
|
|
||||||
: [];
|
|
||||||
if (!shiftKey) {
|
|
||||||
const dialogClosed = new Promise((resolve, _) => {
|
const dialogClosed = new Promise((resolve, _) => {
|
||||||
new RollSelectionDialog(this.system.experiences, this.system.resources.hope.value, resolve).render(
|
this.type === 'character'
|
||||||
true
|
? new RollSelectionDialog(
|
||||||
);
|
this.system.experiences,
|
||||||
|
this.system.resources.hope.value,
|
||||||
|
resolve
|
||||||
|
).render(true)
|
||||||
|
: new NpcRollSelectionDialog(this.system.experiences, resolve).render(true);
|
||||||
});
|
});
|
||||||
const result = await dialogClosed;
|
rollConfig = await dialogClosed;
|
||||||
(hopeDice = result.hope),
|
|
||||||
(fearDice = result.fear),
|
advantage = rollConfig.advantage;
|
||||||
(advantageDice = result.advantage),
|
hopeDice = rollConfig.hope;
|
||||||
(disadvantageDice = result.disadvantage);
|
fearDice = rollConfig.fear;
|
||||||
result.experiences.forEach(x =>
|
|
||||||
|
rollConfig.experiences.forEach(x =>
|
||||||
modifiers.push({
|
modifiers.push({
|
||||||
value: x.value,
|
value: x.value,
|
||||||
label: x.value >= 0 ? `+${x.value}` : `-${x.value}`,
|
label: x.value >= 0 ? `+${x.value}` : `-${x.value}`,
|
||||||
|
|
@ -334,60 +300,123 @@ export default class DhpActor extends Actor {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
if (this.type === 'character') {
|
||||||
|
const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
||||||
|
|
||||||
if (automateHope && result.hopeUsed) {
|
if (automateHope && result.hopeUsed) {
|
||||||
await this.update({
|
await this.update({
|
||||||
'system.resources.hope.value': this.system.resources.hope.value - result.hopeUsed
|
'system.resources.hope.value': this.system.resources.hope.value - result.hopeUsed
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const roll = new Roll(
|
|
||||||
`1${hopeDice} + 1${fearDice}${advantageDice ? ` + 1${advantageDice}` : disadvantageDice ? ` - 1${disadvantageDice}` : ''} ${modifiers.map(x => `+ ${x.value}`).join(' ')}`
|
|
||||||
);
|
|
||||||
let rollResult = await roll.evaluate();
|
|
||||||
setDiceSoNiceForDualityRoll(rollResult, advantageDice, disadvantageDice);
|
|
||||||
|
|
||||||
const hope = rollResult.dice[0].results[0].result;
|
if (this.type === 'character') {
|
||||||
const fear = rollResult.dice[1].results[0].result;
|
formula = `1${hopeDice} + 1${fearDice}${advantage === true ? ` + 1d6` : advantage === false ? ` - 1d6` : ''}`;
|
||||||
const advantage = advantageDice ? rollResult.dice[2].results[0].result : null;
|
} else {
|
||||||
const disadvantage = disadvantageDice ? rollResult.dice[2].results[0].result : null;
|
formula = `${advantage === true || advantage === false ? 2 : 1}d20${advantage === true ? 'kh' : advantage === false ? 'kl' : ''}`;
|
||||||
|
|
||||||
if (disadvantage) {
|
|
||||||
rollResult = { ...rollResult, total: rollResult.total - Math.max(hope, disadvantage) };
|
|
||||||
}
|
|
||||||
if (advantage) {
|
|
||||||
rollResult = { ...rollResult, total: 'Select Hope Die' };
|
|
||||||
}
|
}
|
||||||
|
formula += ` ${modifiers.map(x => `+ ${x.value}`).join(' ')}`;
|
||||||
|
const roll = await Roll.create(formula).evaluate();
|
||||||
|
const dice = roll.dice.flatMap(dice => ({
|
||||||
|
denomination: dice.denomination,
|
||||||
|
number: dice.number,
|
||||||
|
total: dice.total,
|
||||||
|
results: dice.results.map(result => ({ result: result.result, discarded: !result.active }))
|
||||||
|
}));
|
||||||
|
|
||||||
const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
if (this.type === 'character') {
|
||||||
if (automateHope && hope > fear) {
|
setDiceSoNiceForDualityRoll(roll, advantage);
|
||||||
await this.update({
|
hope = roll.dice[0].results[0].result;
|
||||||
'system.resources.hope.value': Math.min(
|
fear = roll.dice[1].results[0].result;
|
||||||
this.system.resources.hope.value + 1,
|
if (
|
||||||
this.system.resources.hope.max
|
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope) &&
|
||||||
)
|
config.roll.type === 'action'
|
||||||
});
|
) {
|
||||||
}
|
if (hope > fear) {
|
||||||
|
await this.update({
|
||||||
if (automateHope && hope === fear) {
|
'system.resources.hope.value': Math.min(
|
||||||
await this.update({
|
this.system.resources.hope.value + 1,
|
||||||
'system.resources': {
|
this.system.resources.hope.max
|
||||||
'hope.value': Math.min(this.system.resources.hope.value + 1, this.system.resources.hope.max),
|
)
|
||||||
'stress.value': Math.max(this.system.resources.stress.value - 1, 0)
|
});
|
||||||
|
} else if (hope === fear) {
|
||||||
|
await this.update({
|
||||||
|
'system.resources': {
|
||||||
|
'hope.value': Math.min(
|
||||||
|
this.system.resources.hope.value + 1,
|
||||||
|
this.system.resources.hope.max
|
||||||
|
),
|
||||||
|
'stress.value': Math.max(this.system.resources.stress.value - 1, 0)
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.checkTarget) {
|
||||||
|
targets = Array.from(game.user.targets).map(x => {
|
||||||
|
const target = {
|
||||||
|
id: x.id,
|
||||||
|
name: x.actor.name,
|
||||||
|
img: x.actor.img,
|
||||||
|
difficulty: x.actor.system.difficulty,
|
||||||
|
evasion: x.actor.system.evasion?.value
|
||||||
|
};
|
||||||
|
|
||||||
|
target.hit = target.difficulty ? roll.total >= target.difficulty : roll.total >= target.evasion;
|
||||||
|
|
||||||
|
return target;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
if (config.chatMessage) {
|
||||||
roll,
|
const configRoll = {
|
||||||
rollResult,
|
title: config.title,
|
||||||
hope: { dice: hopeDice, value: hope },
|
origin: this.id,
|
||||||
fear: { dice: fearDice, value: fear },
|
dice,
|
||||||
advantage: { dice: advantageDice, value: advantage },
|
roll,
|
||||||
disadvantage: { dice: disadvantageDice, value: disadvantage },
|
modifiers: modifiers.filter(x => x.label),
|
||||||
modifiers: modifiers
|
advantageState: advantage
|
||||||
};
|
};
|
||||||
|
if (this.type === 'character') {
|
||||||
|
configRoll.hope = { dice: hopeDice, value: hope };
|
||||||
|
configRoll.fear = { dice: fearDice, value: fear };
|
||||||
|
configRoll.advantage = { dice: advantageDice, value: roll.dice[2]?.results[0].result ?? null };
|
||||||
|
}
|
||||||
|
if (damage) configRoll.damage = damage;
|
||||||
|
if (targets) configRoll.targets = targets;
|
||||||
|
const systemData =
|
||||||
|
this.type === 'character' && !config.roll.simple ? new DHDualityRoll(configRoll) : configRoll,
|
||||||
|
cls = getDocumentClass('ChatMessage'),
|
||||||
|
msg = new cls({
|
||||||
|
type: config.chatMessage.type ?? 'dualityRoll',
|
||||||
|
sound: config.chatMessage.mute ? null : CONFIG.sounds.dice,
|
||||||
|
system: systemData,
|
||||||
|
content: config.chatMessage.template,
|
||||||
|
rolls: [roll]
|
||||||
|
});
|
||||||
|
|
||||||
|
await cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
return roll;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatRollModifier(roll) {
|
||||||
|
const modifier = roll.modifier !== null ? Number.parseInt(roll.modifier) : null;
|
||||||
|
return modifier !== null
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
value: modifier,
|
||||||
|
label: roll.label
|
||||||
|
? modifier >= 0
|
||||||
|
? `${roll.label} +${modifier}`
|
||||||
|
: `${roll.label} ${modifier}`
|
||||||
|
: null,
|
||||||
|
title: roll.label
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
async damageRoll(title, damage, targets, shiftKey) {
|
async damageRoll(title, damage, targets, shiftKey) {
|
||||||
|
|
@ -417,7 +446,11 @@ export default class DhpActor extends Actor {
|
||||||
for (var i = 0; i < rollResult.terms.length; i++) {
|
for (var i = 0; i < rollResult.terms.length; i++) {
|
||||||
const term = rollResult.terms[i];
|
const term = rollResult.terms[i];
|
||||||
if (term.faces) {
|
if (term.faces) {
|
||||||
dice.push({ type: `d${term.faces}`, rolls: term.results.map(x => x.result) });
|
dice.push({
|
||||||
|
type: `d${term.faces}`,
|
||||||
|
rolls: term.results.map(x => x.result),
|
||||||
|
total: term.results.reduce((acc, x) => acc + x.result, 0)
|
||||||
|
});
|
||||||
} else if (term.operator) {
|
} else if (term.operator) {
|
||||||
} else if (term.number) {
|
} else if (term.number) {
|
||||||
const operator = i === 0 ? '' : rollResult.terms[i - 1].operator;
|
const operator = i === 0 ? '' : rollResult.terms[i - 1].operator;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,18 @@
|
||||||
export default class DhpItem extends Item {
|
export default class DhpItem extends Item {
|
||||||
prepareData() {
|
/** @inheritdoc */
|
||||||
super.prepareData();
|
getEmbeddedDocument(embeddedName, id, { invalid = false, strict = false } = {}) {
|
||||||
|
const systemEmbeds = this.system.constructor.metadata.embedded ?? {};
|
||||||
|
if (embeddedName in systemEmbeds) {
|
||||||
|
const path = `system.${systemEmbeds[embeddedName]}`;
|
||||||
|
return foundry.utils.getProperty(this, path).get(id) ?? null;
|
||||||
|
}
|
||||||
|
return super.getEmbeddedDocument(embeddedName, id, { invalid, strict });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @inheritDoc */
|
||||||
|
prepareEmbeddedDocuments() {
|
||||||
|
super.prepareEmbeddedDocuments();
|
||||||
|
for (const action of this.system.actions ?? []) action.prepareData();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -27,10 +39,6 @@ export default class DhpItem extends Item {
|
||||||
return ['weapon', 'armor', 'miscellaneous', 'consumable'].includes(this.type);
|
return ['weapon', 'armor', 'miscellaneous', 'consumable'].includes(this.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUpdate(data, options, userId) {
|
|
||||||
super._onUpdate(data, options, userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async createDialog(data = {}, { parent = null, pack = null, ...options } = {}) {
|
static async createDialog(data = {}, { parent = null, pack = null, ...options } = {}) {
|
||||||
const documentName = this.metadata.name;
|
const documentName = this.metadata.name;
|
||||||
const types = game.documentTypes[documentName].filter(t => t !== CONST.BASE_DOCUMENT_TYPE);
|
const types = game.documentTypes[documentName].filter(t => t !== CONST.BASE_DOCUMENT_TYPE);
|
||||||
|
|
@ -90,4 +98,47 @@ export default class DhpItem extends Item {
|
||||||
options
|
options
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async selectActionDialog() {
|
||||||
|
const content = await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/views/actionSelect.hbs',
|
||||||
|
{ actions: this.system.actions }
|
||||||
|
),
|
||||||
|
title = 'Select Action',
|
||||||
|
type = 'div',
|
||||||
|
data = {};
|
||||||
|
return Dialog.prompt({
|
||||||
|
title,
|
||||||
|
// label: title,
|
||||||
|
content,
|
||||||
|
type,
|
||||||
|
callback: html => {
|
||||||
|
const form = html[0].querySelector('form'),
|
||||||
|
fd = new foundry.applications.ux.FormDataExtended(form);
|
||||||
|
return this.system.actions.find(a => a._id === fd.object.actionId);
|
||||||
|
},
|
||||||
|
rejectClose: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async use(event) {
|
||||||
|
const actions = this.system.actions;
|
||||||
|
let response;
|
||||||
|
if (actions?.length) {
|
||||||
|
let action = actions[0];
|
||||||
|
if (actions.length > 1 && !event?.shiftKey) {
|
||||||
|
// Actions Choice Dialog
|
||||||
|
action = await this.selectActionDialog();
|
||||||
|
}
|
||||||
|
if (action) response = action.use(event);
|
||||||
|
// Check Target
|
||||||
|
// If action.roll => Roll Dialog
|
||||||
|
// Else If action.cost => Cost Dialog
|
||||||
|
// Then
|
||||||
|
// Apply Cost
|
||||||
|
// Apply Effect
|
||||||
|
}
|
||||||
|
// Display Item Card in chat
|
||||||
|
return response;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,21 +9,26 @@ export function dualityRollEnricher(match, _options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDualityMessage(roll) {
|
export function getDualityMessage(roll) {
|
||||||
const attributeLabel =
|
const traitLabel =
|
||||||
roll.attribute && abilities[roll.attribute]
|
roll.trait && abilities[roll.trait]
|
||||||
? game.i18n.format('DAGGERHEART.General.Check', {
|
? game.i18n.format('DAGGERHEART.General.Check', {
|
||||||
check: game.i18n.localize(abilities[roll.attribute].label)
|
check: game.i18n.localize(abilities[roll.trait].label)
|
||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
const label = attributeLabel ?? game.i18n.localize('DAGGERHEART.General.Duality');
|
|
||||||
|
const label = traitLabel ?? game.i18n.localize('DAGGERHEART.General.Duality');
|
||||||
|
const dataLabel = traitLabel
|
||||||
|
? game.i18n.localize(abilities[roll.trait].label)
|
||||||
|
: game.i18n.localize('DAGGERHEART.General.Duality');
|
||||||
|
|
||||||
const dualityElement = document.createElement('span');
|
const dualityElement = document.createElement('span');
|
||||||
dualityElement.innerHTML = `
|
dualityElement.innerHTML = `
|
||||||
<button class="duality-roll-button"
|
<button class="duality-roll-button"
|
||||||
data-label="${label}"
|
data-title="${label}"
|
||||||
|
data-label="${dataLabel}"
|
||||||
data-hope="${roll.hope ?? 'd12'}"
|
data-hope="${roll.hope ?? 'd12'}"
|
||||||
data-fear="${roll.fear ?? 'd12'}"
|
data-fear="${roll.fear ?? 'd12'}"
|
||||||
${roll.attribute && abilities[roll.attribute] ? `data-attribute="${roll.attribute}"` : ''}
|
${roll.trait && abilities[roll.trait] ? `data-trait="${roll.trait}"` : ''}
|
||||||
${roll.advantage ? 'data-advantage="true"' : ''}
|
${roll.advantage ? 'data-advantage="true"' : ''}
|
||||||
${roll.disadvantage ? 'data-disadvantage="true"' : ''}
|
${roll.disadvantage ? 'data-disadvantage="true"' : ''}
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -122,14 +122,16 @@ export const getCommandTarget = () => {
|
||||||
return target;
|
return target;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setDiceSoNiceForDualityRoll = (rollResult, advantage, disadvantage) => {
|
export const setDiceSoNiceForDualityRoll = (rollResult, advantageState) => {
|
||||||
const diceSoNicePresets = getDiceSoNicePresets();
|
const diceSoNicePresets = getDiceSoNicePresets();
|
||||||
rollResult.dice[0].options.appearance = diceSoNicePresets.hope;
|
rollResult.dice[0].options.appearance = diceSoNicePresets.hope;
|
||||||
rollResult.dice[1].options.appearance = diceSoNicePresets.fear;
|
rollResult.dice[1].options.appearance = diceSoNicePresets.fear;
|
||||||
if (advantage) {
|
if (rollResult.dice[2]) {
|
||||||
rollResult.dice[2].options.appearance = diceSoNicePresets.advantage;
|
if (advantageState === true) {
|
||||||
} else if (disadvantage) {
|
rollResult.dice[2].options.appearance = diceSoNicePresets.advantage;
|
||||||
rollResult.dice[2].options.appearance = diceSoNicePresets.disadvantage;
|
} else if (advantageState === false) {
|
||||||
|
rollResult.dice[2].options.appearance = diceSoNicePresets.disadvantage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -222,3 +224,14 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Fix on Foundry native formula replacement for DH
|
||||||
|
const nativeReplaceFormulaData = Roll.replaceFormulaData;
|
||||||
|
Roll.replaceFormulaData = function (formula, data, { missing, warn = false } = {}) {
|
||||||
|
const terms = [
|
||||||
|
{ term: 'prof', default: 1 },
|
||||||
|
{ term: 'cast', default: 1 }
|
||||||
|
];
|
||||||
|
formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula);
|
||||||
|
return nativeReplaceFormulaData(formula, data, { missing, warn });
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -452,6 +452,37 @@ div.daggerheart.views.multiclass {
|
||||||
&.open {
|
&.open {
|
||||||
max-height: initial;
|
max-height: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.multi-display {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
.form-group {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
label {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
.form-fields {
|
||||||
|
flex: 3;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-form-array {
|
||||||
|
border: 1px solid var(--color-fieldset-border);
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2216,6 +2216,34 @@ div.daggerheart.views.multiclass {
|
||||||
.daggerheart.views.action .action-category .action-category-data.open {
|
.daggerheart.views.action .action-category .action-category-data.open {
|
||||||
max-height: initial;
|
max-height: initial;
|
||||||
}
|
}
|
||||||
|
.daggerheart.views.action .action-category .action-category-data .multi-display {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.daggerheart.views.action .action-category .action-category-data .multi-display .form-group {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.daggerheart.views.action .action-category .action-category-data .form-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.daggerheart.views.action .action-category .action-category-data .form-group label {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
.daggerheart.views.action .action-category .action-category-data .form-group .form-fields {
|
||||||
|
flex: 3;
|
||||||
|
}
|
||||||
|
.daggerheart.views.action .action-category .action-category-data .form-group img {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
|
.daggerheart.views.action .action-category .action-category-data .data-form-array {
|
||||||
|
border: 1px solid var(--color-fieldset-border);
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
.daggerheart.views.ancestry-selection .ancestry-section {
|
.daggerheart.views.ancestry-selection .ancestry-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -3706,3 +3734,34 @@ div.daggerheart.views.multiclass {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
filter: drop-shadow(0 0 3px red);
|
filter: drop-shadow(0 0 3px red);
|
||||||
}
|
}
|
||||||
|
.unlist {
|
||||||
|
list-style: none;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
.list-select {
|
||||||
|
margin: 1rem;
|
||||||
|
}
|
||||||
|
.list-select li:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
}
|
||||||
|
.list-select li label {
|
||||||
|
padding: 4px 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.list-select li label > span {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: var(--font-size-16);
|
||||||
|
}
|
||||||
|
dh-icon,
|
||||||
|
dh-icon > img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: x-large;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,3 +130,39 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unlist {
|
||||||
|
list-style: none;
|
||||||
|
padding-inline-start: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-select {
|
||||||
|
margin: 1rem;
|
||||||
|
li {
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px solid #bbb;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
padding: 4px 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
> span {
|
||||||
|
flex: 1;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: var(--font-size-16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dh-icon,
|
||||||
|
dh-icon > img {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: x-large;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,32 @@
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="dice-flavor">{{localize "DAGGERHEART.Chat.AttackRoll.Title" attack=this.title}}</div>
|
<div class="dice-flavor">{{localize "DAGGERHEART.Chat.AttackRoll.Title" attack=this.title}}</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{roll}}</div>
|
<div class="dice-formula">{{roll.formula}}</div>
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<section class="tooltip-part">
|
<section class="tooltip-part">
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
{{#each dice}}
|
||||||
<span class="part-formula">{{this.dice.rolls.length}}{{this.dice.type}}</span>
|
<header class="part-header flexrow">
|
||||||
<span class="part-total">{{this.dice.rollTotal}}</span>
|
<span class="part-formula">{{number}}{{denomination}}</span>
|
||||||
</header>
|
<span class="part-total">{{total}}</span>
|
||||||
<div class="flexrow">
|
</header>
|
||||||
<ol class="dice-rolls">
|
<div class="flexrow">
|
||||||
{{#each this.dice.rolls}}
|
<ol class="dice-rolls">
|
||||||
<li class="roll die {{../dice.type}} {{#if this.discarded}}discarded{{/if}} min">{{this.value}}</li>
|
{{#each results}}
|
||||||
{{/each}}
|
<li class="roll die {{../denomination}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
|
||||||
</ol>
|
{{/each}}
|
||||||
<div class="attack-roll-advantage-container">{{#if (eq this.advantageState 1)}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq this.advantageState 2)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}</div>
|
</ol>
|
||||||
</div>
|
<div class="attack-roll-advantage-container">{{#if ../advantageState}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../advantageState false)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dice-total">
|
<div class="dice-total">
|
||||||
<div class="dice-total-value">{{this.total}}</div>
|
<div class="dice-total-value">{{roll.total}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if (gt targets.length 0)}}
|
{{#if (gt targets.length 0)}}
|
||||||
<div class="target-section">
|
<div class="target-section">
|
||||||
|
|
@ -39,7 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<button class="duality-action" data-value="{{this.total.normal}}" data-damage="{{this.damage.value}}" data-damage-type="{{this.damage.type}}" {{#if this.damage.disabled}}disabled{{/if}}><span>Roll Damage</span></button>
|
<button class="duality-action" data-value="{{roll.total}}" data-damage="{{damage.value}}" data-damage-type="{{damage.type}}"{{#if damage.disabled}} disabled{{/if}}><span>Roll Damage</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,22 +1,33 @@
|
||||||
<div class="dice-roll daggerheart chat roll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{roll}}</div>
|
<div class="dice-formula">{{roll.formula}}</div>
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<ol class="dice-rolls">
|
<ol class="dice-rolls">
|
||||||
<div class="dice-hope-container">
|
<div class="dice-hope-container">
|
||||||
{{#each diceResults}}
|
{{#each dice}}
|
||||||
<li class="roll die d20 {{#if this.discarded}}discarded{{/if}}">{{this.value}}</li>
|
<header class="part-header flexrow">
|
||||||
{{/each}}
|
<span class="part-formula">{{number}}{{denomination}}</span>
|
||||||
|
<span class="part-total">{{total}}</span>
|
||||||
|
</header>
|
||||||
|
<div class="flexrow">
|
||||||
|
<ol class="dice-rolls">
|
||||||
|
{{#each results}}
|
||||||
|
<li class="roll die {{../denomination}}{{#if discarded}} discarded{{/if}} min">{{result}}</li>
|
||||||
|
{{/each}}
|
||||||
|
</ol>
|
||||||
|
<div class="attack-roll-advantage-container">{{#if ../advantageState}}{{localize "DAGGERHEART.General.Advantage.Full"}}{{/if}}{{#if (eq ../advantageState false)}}{{localize "DAGGERHEART.General.Disadvantage.Full"}}{{/if}}</div>
|
||||||
|
</div>
|
||||||
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modifiers-container">
|
<div class="modifiers-container">
|
||||||
{{#each modifiers}}
|
{{#each modifiers}}
|
||||||
<li class="modifier-value" data-value="{{this.value}}" title="{{this.title}}">{{this.label}}</li>
|
<li class="modifier-value" data-value="{{value}}" title="{{title}}">{{label}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-total">
|
<div class="dice-total">
|
||||||
<div class="dice-total-value">{{total}}</div>
|
<div class="dice-total-value">{{roll.total}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1,27 +1,27 @@
|
||||||
{{#if this.colorful}}
|
{{#if colorful}}
|
||||||
<div class="daggerheart chat roll" data-action="expandRoll">
|
<div class="daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="duality-data">
|
<div class="duality-data">
|
||||||
<div class="duality-title">
|
<div class="duality-title">
|
||||||
<div>{{localize "DAGGERHEART.Chat.AttackRoll.Title" attack=this.title}}</div>
|
<div>{{localize "DAGGERHEART.Chat.AttackRoll.Title" attack=title}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="duality-modifiers">
|
<div class="duality-modifiers">
|
||||||
{{#each this.modifiers}}
|
{{#each modifiers}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{this.label}}
|
{{label}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#if this.advantage.value}}
|
{{#if advantageState}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{localize "DAGGERHEART.General.Advantage.Full"}}
|
{{localize "DAGGERHEART.General.Advantage.Full"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.disadvantage.value}}
|
{{#if (eq advantageState false)}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{localize "DAGGERHEART.General.Disadvantage.Full"}}
|
{{localize "DAGGERHEART.General.Disadvantage.Full"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="duality-line {{#if (not this.damage.value)}}simple{{/if}}">
|
<div class="duality-line {{#if (not damage.value)}}simple{{/if}}">
|
||||||
<div class="dice-outer-container">
|
<div class="dice-outer-container">
|
||||||
<div class="dice-container">
|
<div class="dice-container">
|
||||||
<div class="dice-title">{{localize "DAGGERHEART.General.Hope"}}</div>
|
<div class="dice-title">{{localize "DAGGERHEART.General.Hope"}}</div>
|
||||||
|
|
@ -41,27 +41,27 @@
|
||||||
<div class="dice-value">{{fear.value}}</div>
|
<div class="dice-value">{{fear.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if this.advantage.value}}
|
{{#if advantageState}}
|
||||||
<div class="advantage-container advantage">
|
<div class="advantage-container advantage">
|
||||||
<div class="dice-wrapper">
|
<div class="dice-wrapper">
|
||||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
||||||
<div class="dice-value">{{this.advantage.value}}</div>
|
<div class="dice-value">{{advantage.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.disadvantage.value}}
|
{{#if (eq advantageState false)}}
|
||||||
<div class="advantage-container disadvantage">
|
<div class="advantage-container disadvantage">
|
||||||
<div class="dice-wrapper">
|
<div class="dice-wrapper">
|
||||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
||||||
<div class="dice-value">{{this.disadvantage.value}}</div>
|
<div class="dice-value">{{advantage.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.modifierTotal.value}}<div class="duality-modifier">{{this.modifierTotal.label}}</div>{{/if}}
|
{{#if modifierTotal.value}}<div class="duality-modifier">{{modifierTotal.label}}</div>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{#if (not this.damage.value)}}
|
{{#if (not damage.value)}}
|
||||||
<div class="duality-result">
|
<div class="duality-result">
|
||||||
<div>{{this.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
<div>{{roll.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -78,18 +78,18 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.damage.value}}
|
{{#if damage.value}}
|
||||||
<div class="duality-actions">
|
<div class="duality-actions">
|
||||||
<button class="duality-action" data-value="{{this.total}}" data-damage="{{this.damage.value}}" data-damage-type="{{this.damage.type}}" {{#if this.damage.disabled}}disabled{{/if}}><span>Roll Damage</span></button>
|
<button class="duality-action" data-value="{{roll.total}}" data-damage="{{damage.value}}" data-damage-type="{{damage.type}}" {{#if damage.disabled}}disabled{{/if}}><span>Roll Damage</span></button>
|
||||||
<div class="duality-result">
|
<div class="duality-result">
|
||||||
<div>{{this.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
<div>{{roll.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="dice-flavor">{{localize "DAGGERHEART.Chat.AttackRoll.Title" attack=this.title}}</div>
|
<div class="dice-flavor">{{localize "DAGGERHEART.Chat.AttackRoll.Title" attack=title}}</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{roll}}</div>
|
<div class="dice-formula">{{roll}}</div>
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@
|
||||||
|
|
|
|
||||||
<span>1{{fear.dice}}</span>
|
<span>1{{fear.dice}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="part-total">{{this.diceTotal}}</span>
|
<span class="part-total">{{diceTotal}}</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<ol class="dice-rolls duality">
|
<ol class="dice-rolls duality">
|
||||||
|
|
@ -112,7 +112,7 @@
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if advantage.value}}
|
{{#if (eq advantageState 1)}}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">
|
<span class="part-formula">
|
||||||
|
|
@ -127,17 +127,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if disadvantage.value}}
|
{{#if (eq advantageState 2)}}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">
|
<span class="part-formula">
|
||||||
<span>1{{disadvantage.dice}}</span>
|
<span>1{{advantage.dice}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="part-total">{{disadvantage.value}}</span>
|
<span class="part-total">{{advantage.value}}</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<ol class="dice-rolls">
|
<ol class="dice-rolls">
|
||||||
<li class="roll die {{disadvantage.dice}} hope min">{{disadvantage.value}}</li>
|
<li class="roll die {{advantage.dice}} hope min">{{advantage.value}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -148,7 +148,7 @@
|
||||||
<div class="dice-total duality {{#if fear.discarded}}hope{{else}}{{#if hope.discarded}}fear{{else}}critical{{/if}}{{/if}}">
|
<div class="dice-total duality {{#if fear.discarded}}hope{{else}}{{#if hope.discarded}}fear{{else}}critical{{/if}}{{/if}}">
|
||||||
<div class="dice-total-label">{{totalLabel}}</div>
|
<div class="dice-total-label">{{totalLabel}}</div>
|
||||||
<div class="dice-total-value">
|
<div class="dice-total-value">
|
||||||
{{this.total}}
|
{{roll.total}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if (gt targets.length 0)}}
|
{{#if (gt targets.length 0)}}
|
||||||
|
|
@ -164,7 +164,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="dice-actions">
|
<div class="dice-actions">
|
||||||
<button class="duality-action" data-value="{{this.total}}" data-damage="{{this.damage.value}}" data-damage-type="{{this.damage.type}}" {{#if this.damage.disabled}}disabled{{/if}}><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollDamage"}}</span></button>
|
<button class="duality-action" data-value="{{roll.total}}" data-damage="{{damage.value}}" data-damage-type="{{damage.type}}" {{#if damage.disabled}}disabled{{/if}}><span>{{localize "DAGGERHEART.Chat.AttackRoll.RollDamage"}}</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="dice-flavor">{{this.title}}</div>
|
<div class="dice-flavor">{{title}}</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{this.roll}}</div>
|
<div class="dice-formula">{{roll}}</div>
|
||||||
|
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
|
@ -9,12 +9,12 @@
|
||||||
{{#each dice}}
|
{{#each dice}}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">{{this.rolls.length}}{{this.type}}</span>
|
<span class="part-formula">{{rolls.length}}{{type}}</span>
|
||||||
|
|
||||||
<span class="part-total">{{this.rollTotal}}</span>
|
<span class="part-total">{{this.total}}</span>
|
||||||
</header>
|
</header>
|
||||||
<ol class="dice-rolls">
|
<ol class="dice-rolls">
|
||||||
{{#each this.rolls}}
|
{{#each rolls}}
|
||||||
<li class="roll die {{../type}} min">{{this}}</li>
|
<li class="roll die {{../type}} min">{{this}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ol>
|
</ol>
|
||||||
|
|
@ -23,9 +23,9 @@
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="dice-total">{{this.damage.total}}</div>
|
<div class="dice-total">{{damage.total}}</div>
|
||||||
<div class="dice-actions">
|
<div class="dice-actions">
|
||||||
<button class="damage-button" data-target-hit="true" {{#if (eq this.targets.length 0)}}disabled{{/if}}>{{localize "DAGGERHEART.Chat.DamageRoll.DealDamageToTargets"}}</button>
|
<button class="damage-button" data-target-hit="true" {{#if (eq targets.length 0)}}disabled{{/if}}>{{localize "DAGGERHEART.Chat.DamageRoll.DealDamageToTargets"}}</button>
|
||||||
<button class="damage-button">{{localize "DAGGERHEART.Chat.DamageRoll.DealDamage"}}</button>
|
<button class="damage-button">{{localize "DAGGERHEART.Chat.DamageRoll.DealDamage"}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,27 @@
|
||||||
{{#if this.colorful}}
|
{{#if colorful}}
|
||||||
<div class="daggerheart chat roll" data-action="expandRoll">
|
<div class="daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="duality-data">
|
<div class="duality-data">
|
||||||
<div class="duality-title">
|
<div class="duality-title">
|
||||||
<div>{{this.title}}</div>
|
<div>{{title}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="duality-modifiers">
|
<div class="duality-modifiers">
|
||||||
{{#each this.modifiers}}
|
{{#each modifiers}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{this.label}}
|
{{label}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#if this.advantage.value}}
|
{{#if advantageState}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{localize "DAGGERHEART.General.Advantage.Full"}}
|
{{localize "DAGGERHEART.General.Advantage.Full"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.disadvantage.value}}
|
{{#if (eq advantageState false)}}
|
||||||
<div class="duality-modifier">
|
<div class="duality-modifier">
|
||||||
{{localize "DAGGERHEART.General.Disadvantage.Full"}}
|
{{localize "DAGGERHEART.General.Disadvantage.Full"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="duality-line {{#if (not this.damage.value)}}simple{{/if}}">
|
<div class="duality-line {{#if (not damage.value)}}simple{{/if}}">
|
||||||
<div class="dice-outer-container">
|
<div class="dice-outer-container">
|
||||||
<div class="dice-container">
|
<div class="dice-container">
|
||||||
<div class="dice-title">{{localize "DAGGERHEART.General.Hope"}}</div>
|
<div class="dice-title">{{localize "DAGGERHEART.General.Hope"}}</div>
|
||||||
|
|
@ -41,32 +41,32 @@
|
||||||
<div class="dice-value">{{fear.value}}</div>
|
<div class="dice-value">{{fear.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if this.advantage.value}}
|
{{#if advantageState}}
|
||||||
<div class="advantage-container advantage">
|
<div class="advantage-container advantage">
|
||||||
<div class="dice-wrapper">
|
<div class="dice-wrapper">
|
||||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
||||||
<div class="dice-value">{{this.advantage.value}}</div>
|
<div class="dice-value">{{advantage.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.disadvantage.value}}
|
{{#if (eq advantageState false)}}
|
||||||
<div class="advantage-container disadvantage">
|
<div class="advantage-container disadvantage">
|
||||||
<div class="dice-wrapper">
|
<div class="dice-wrapper">
|
||||||
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
<img class="dice" src="../icons/svg/d6-grey.svg"/>
|
||||||
<div class="dice-value">{{this.disadvantage.value}}</div>
|
<div class="dice-value">{{advantage.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.modifierTotal.value}}<div class="duality-modifier">{{this.modifierTotal.label}}</div>{{/if}}
|
{{#if modifierTotal.value}}<div class="duality-modifier">{{modifierTotal.label}}</div>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{#if (not this.damage.value)}}
|
{{#if (not damage.value)}}
|
||||||
<div class="duality-result">
|
<div class="duality-result">
|
||||||
<div>{{this.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
<div>{{roll.total}} {{#if (eq dualityResult 1)}}With Hope{{else}}{{#if (eq dualityResult 2)}}With Fear{{else}}Critical Success{{/if}}{{/if}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if this.damage.value}}
|
{{#if damage.value}}
|
||||||
<div class="duality-actions">
|
<div class="duality-actions">
|
||||||
<div></div>
|
<div></div>
|
||||||
<div class="duality-result">
|
<div class="duality-result">
|
||||||
|
|
@ -77,9 +77,9 @@
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
<div class="dice-roll daggerheart chat roll" data-action="expandRoll">
|
||||||
<div class="dice-flavor">{{this.title}}</div>
|
<div class="dice-flavor">{{title}}</div>
|
||||||
<div class="dice-result">
|
<div class="dice-result">
|
||||||
<div class="dice-formula">{{roll}}</div>
|
<div class="dice-formula">{{roll.formula}}</div>
|
||||||
<div class="dice-tooltip">
|
<div class="dice-tooltip">
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<section class="tooltip-part">
|
<section class="tooltip-part">
|
||||||
|
|
@ -90,7 +90,7 @@
|
||||||
|
|
|
|
||||||
<span>1{{fear.dice}}</span>
|
<span>1{{fear.dice}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="part-total">{{this.diceTotal}}</span>
|
<span class="part-total">{{diceTotal}}</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<ol class="dice-rolls duality">
|
<ol class="dice-rolls duality">
|
||||||
|
|
@ -99,7 +99,7 @@
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if advantage.value}}
|
{{#if (eq advantageState 1)}}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">
|
<span class="part-formula">
|
||||||
|
|
@ -114,17 +114,17 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if disadvantage.value}}
|
{{#if (eq advantageState 2)}}
|
||||||
<div class="dice">
|
<div class="dice">
|
||||||
<header class="part-header flexrow">
|
<header class="part-header flexrow">
|
||||||
<span class="part-formula">
|
<span class="part-formula">
|
||||||
<span>1{{disadvantage.dice}}</span>
|
<span>1{{advantage.dice}}</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="part-total">{{disadvantage.value}}</span>
|
<span class="part-total">{{advantage.value}}</span>
|
||||||
</header>
|
</header>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<ol class="dice-rolls">
|
<ol class="dice-rolls">
|
||||||
<li class="roll die {{disadvantage.dice}} hope min">{{disadvantage.value}}</li>
|
<li class="roll die {{advantage.dice}} hope min">{{advantage.value}}</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -135,7 +135,7 @@
|
||||||
<div class="dice-total duality {{#if fear.discarded}}hope{{else}}{{#if hope.discarded}}fear{{else}}critical{{/if}}{{/if}}">
|
<div class="dice-total duality {{#if fear.discarded}}hope{{else}}{{#if hope.discarded}}fear{{else}}critical{{/if}}{{/if}}">
|
||||||
<div class="dice-total-label">{{totalLabel}}</div>
|
<div class="dice-total-label">{{totalLabel}}</div>
|
||||||
<div class="dice-total-value">
|
<div class="dice-total-value">
|
||||||
{{total}}
|
{{roll.total}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,15 @@
|
||||||
data-tab='{{tabs.information.id}}'
|
data-tab='{{tabs.information.id}}'
|
||||||
data-group='{{tabs.information.group}}'
|
data-group='{{tabs.information.group}}'
|
||||||
>
|
>
|
||||||
<fieldset>
|
{{!-- <fieldset>
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.Description" }}</legend>
|
<legend>{{localize "DAGGERHEART.Sheets.Adversary.FIELDS.description.label" }}</legend>
|
||||||
|
|
||||||
{{formGroup systemFields.description value=source.system.description}}
|
{{formInput systemFields.description value=source.system.description}}
|
||||||
</fieldset>
|
</fieldset> --}}
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.MotivesAndTactics" }}</legend>
|
<legend>{{localize "DAGGERHEART.Sheets.Adversary.FIELDS.motivesAndTactics.label" }}</legend>
|
||||||
|
|
||||||
{{formGroup systemFields.motivesAndTactics value=source.system.motivesAndTactics}}
|
{{formInput systemFields.motivesAndTactics value=source.system.motivesAndTactics}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
data-group='{{tabs.main.group}}'
|
data-group='{{tabs.main.group}}'
|
||||||
>
|
>
|
||||||
<div class="adversary-container">
|
<div class="adversary-container">
|
||||||
|
<button data-action="reactionRoll">Reaction Test</button>
|
||||||
<fieldset class="two-columns even">
|
<fieldset class="two-columns even">
|
||||||
<legend>{{localize "DAGGERHEART.Sheets.Adversary.General"}}</legend>
|
<legend>{{localize "DAGGERHEART.Sheets.Adversary.General"}}</legend>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<li class="item inventory-item">
|
<li class="item inventory-item">
|
||||||
<div class="inventory-row" data-item-id="{{item.uuid}}">
|
<div class="inventory-row" data-item-id="{{item.uuid}}">
|
||||||
<div class="inventory-item-title-container">
|
<div class="inventory-item-title-container">
|
||||||
<div data-action="viewObject" data-value="{{item.uuid}}" class="inventory-item-title">
|
<div data-action="useItem" data-value="{{item.uuid}}" class="inventory-item-title">
|
||||||
<img src="{{item.img}}" />
|
<img src="{{item.img}}" />
|
||||||
{{item.name}}
|
{{item.name}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
3
templates/sheets/pseudo-documents/header.hbs
Normal file
3
templates/sheets/pseudo-documents/header.hbs
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
<header>
|
||||||
|
<input type="text" name="name" value="{{document.name}}">
|
||||||
|
</header>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<div>
|
<div>
|
||||||
<header>
|
<header>
|
||||||
{{formField fields.name value=source.name label="Name" name="name" rootId=partId}}
|
{{!-- {{formField fields.name value=source.name label="Name" name="name" rootId=partId}} --}}
|
||||||
<nav class="sheet-tabs tabs">
|
<nav class="sheet-tabs tabs">
|
||||||
{{#each tabs as |tab|}}
|
{{#each tabs as |tab|}}
|
||||||
<a class="{{tab.cssClass}}" data-action="tab" data-group="{{tab.group}}" data-tab="{{tab.id}}">
|
<a class="{{tab.cssClass}}" data-action="tab" data-group="{{tab.group}}" data-tab="{{tab.id}}">
|
||||||
|
|
@ -11,51 +11,30 @@
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
<section>
|
<section>
|
||||||
<div class="tab {{this.tabs.effects.cssClass}}" data-group="primary" data-tab="effects">
|
<div class="tab {{this.tabs.base.cssClass}}" data-group="primary" data-tab="base">
|
||||||
<fieldset class="action-category">
|
<fieldset class="action-category">
|
||||||
<legend class="action-category-label" data-action="toggleSection" data-section="damage">
|
<legend class="action-category-label" data-action="toggleSection" data-section="identity">
|
||||||
<div>Damage</div>
|
<div>Identity</div>
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="action-category-data open">
|
<div class="action-category-data open">
|
||||||
{{formField fields.damage.fields.type value=source.damage.type label="Damage Type" name="damage.type" rootId=partId localize=true}}
|
{{formField fields.name value=source.name label="Name" name="name"}}
|
||||||
{{formField fields.damage.fields.value value=source.damage.value label="Damage" name="damage.value" rootId=partId localize=true}}
|
{{formField fields.img value=source.img label="Icon" name="img"}}
|
||||||
</div>
|
{{formField fields.actionType value=source.actionType label="Type" name="actionType" localize=true}}
|
||||||
</fieldset>
|
|
||||||
<fieldset class="action-category">
|
|
||||||
<legend class="action-category-label" data-action="toggleSection" data-section="healing">
|
|
||||||
<div>Healing</div>
|
|
||||||
</legend>
|
|
||||||
|
|
||||||
<div class="action-category-data open ">
|
|
||||||
{{formField fields.healing.fields.type value=source.healing.type label="Healing Type" name="healing.type" rootId=partId localize=true}}
|
|
||||||
{{formField fields.healing.fields.value value=source.healing.value label="Healing" name="healing.value" rootId=partId localize=true}}
|
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
{{#if fields.roll}}{{> 'systems/daggerheart/templates/views/actionTypes/roll.hbs' fields=fields.roll.fields source=source.roll}}{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab {{this.tabs.useage.cssClass}}" data-group="primary" data-tab="useage">
|
<div class="tab {{this.tabs.config.cssClass}}" data-group="primary" data-tab="config">
|
||||||
<fieldset class="action-category">
|
{{> 'systems/daggerheart/templates/views/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}}
|
||||||
<legend class="action-category-label" data-action="toggleSection" data-section="cost">
|
{{> 'systems/daggerheart/templates/views/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost}}
|
||||||
<div>Cost</div>
|
{{#if fields.target}}{{> 'systems/daggerheart/templates/views/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}}{{/if}}
|
||||||
</legend>
|
|
||||||
|
|
||||||
<div class="action-category-data open ">
|
|
||||||
{{formField fields.cost.fields.type value=source.cost.type label="Cost Type" name="cost.type" rootId=partId}}
|
|
||||||
{{formField fields.cost.fields.value value=source.cost.value label="Value" name="cost.value" rootId=partId}}
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
|
||||||
|
|
||||||
{{formField fields.target.fields.type value=source.target.type label="Target Type" name="target.type" rootId=partId}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="tab {{this.tabs.conditions.cssClass}}" data-group="primary" data-tab="conditions">
|
<div class="tab {{this.tabs.effect.cssClass}}" data-group="primary" data-tab="effect">
|
||||||
{{!-- <h2>
|
{{#if fields.damage}}{{> 'systems/daggerheart/templates/views/actionTypes/damage.hbs' fields=fields.damage.fields.parts.element.fields source=source.damage}}{{/if}}
|
||||||
{{localize "Conditions"}}
|
{{#if fields.healing}}{{> 'systems/daggerheart/templates/views/actionTypes/healing.hbs' fields=fields.healing.fields source=source.healing}}{{/if}}
|
||||||
<select class="effect-select">
|
{{#if fields.resource}}{{> 'systems/daggerheart/templates/views/actionTypes/resource.hbs' fields=fields.resource.fields source=source.resource}}{{/if}}
|
||||||
{{selectOptions this.config.effectTypes selected=this.selectedEffectType labelAttr="name" localize=true blank=""}}
|
{{#if fields.documentUUID}}{{> 'systems/daggerheart/templates/views/actionTypes/uuid.hbs' fields=fields.documentUUID source=source.documentUUID}}{{/if}}
|
||||||
</select>
|
{{#if fields.effects}}{{> 'systems/daggerheart/templates/views/actionTypes/effect.hbs'}}{{/if}}
|
||||||
<i class="fa-solid fa-plus icon-button {{#if (not this.selectedEffectType)}}disabled{{/if}}" data-action="addCondition"></i>
|
|
||||||
</h2> --}}
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<button type="submit">Save</button>
|
|
||||||
</div>
|
</div>
|
||||||
13
templates/views/actionSelect.hbs
Normal file
13
templates/views/actionSelect.hbs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<form id="item-action-select">
|
||||||
|
<ul class="unlist list-select">
|
||||||
|
{{#each actions}}
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<dh-icon><img src="{{ img }}"></dh-icon>
|
||||||
|
<span>{{ name }}</span>
|
||||||
|
<input type="radio" name="actionId" value="{{_id}}" {{#if (eq @index 0)}}checked{{/if}}>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
13
templates/views/actionType.hbs
Normal file
13
templates/views/actionType.hbs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<form id="action-type-select">
|
||||||
|
<ul class="unlist list-select">
|
||||||
|
{{#each types}}
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<dh-icon class="dh-icon fas {{icon}}"></dh-icon>
|
||||||
|
<span>{{localize name}}</span>
|
||||||
|
<input type="radio" name="type" value="{{id}}" {{#if (eq @index 0)}}checked{{/if}}>
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
21
templates/views/actionTypes/cost.hbs
Normal file
21
templates/views/actionTypes/cost.hbs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="cost">
|
||||||
|
<div>Cost</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open" data-key="cost">
|
||||||
|
<div class="fas fa-plus icon-button" data-action="addElement"></div>
|
||||||
|
{{#each source as |cost index|}}
|
||||||
|
<fieldset>
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField ../fields.type label="Resource" value=cost.type name=(concat "cost." index ".type") localize=true}}
|
||||||
|
{{formField ../fields.value label="Value" value=cost.value name=(concat "cost." index ".value")}}
|
||||||
|
</div>
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField ../fields.scalable label="Scalable" value=cost.scalable name=(concat "cost." index ".scalable")}}
|
||||||
|
{{formField ../fields.step label="Step" value=cost.step name=(concat "cost." index ".step")}}
|
||||||
|
</div>
|
||||||
|
<div class="fas fa-trash" data-action="removeElement" data-index="{{index}}"></div>
|
||||||
|
</fieldset>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
36
templates/views/actionTypes/damage.hbs
Normal file
36
templates/views/actionTypes/damage.hbs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="effects">
|
||||||
|
<div>Damage</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
<div class="fas fa-plus icon-button" data-action="addDamage"></div>
|
||||||
|
{{#if @root.hasBaseDamage}}
|
||||||
|
<div>
|
||||||
|
{{!-- <input type="checkbox" data-action="addBaseDamage"{{#if @root.hasBaseDamage}} checked{{/if}}> --}}
|
||||||
|
{{formField @root.fields.damage.fields.includeBase value=@root.source.damage.includeBase label="Include Item Damage" name="damage.includeBase" }}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#each source.parts as |dmg index|}}
|
||||||
|
{{#with (@root.getRealIndex index) as | realIndex |}}
|
||||||
|
<fieldset{{#if dmg.base}} disabled{{/if}}>
|
||||||
|
{{#unless dmg.base}}
|
||||||
|
{{formField ../../fields.custom.fields.enabled value=dmg.custom.enabled name=(concat "damage.parts." realIndex ".custom.enabled")}}
|
||||||
|
{{/unless}}
|
||||||
|
{{#if dmg.custom.enabled}}
|
||||||
|
{{formField ../../fields.custom.fields.formula value=dmg.custom.formula name=(concat "damage.parts." realIndex ".custom.formula") localize=true}}
|
||||||
|
{{else}}
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField ../../fields.multiplier value=dmg.multiplier name=(concat "damage.parts." realIndex ".multiplier") localize=true}}
|
||||||
|
{{formField ../../fields.dice value=dmg.dice name=(concat "damage.parts." realIndex ".dice")}}
|
||||||
|
{{formField ../../fields.bonus value=dmg.bonus name=(concat "damage.parts." realIndex ".bonus") localize=true}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{formField ../../fields.type value=dmg.type name=(concat "damage.parts." realIndex ".type") localize=true}}
|
||||||
|
<input type="hidden" name="damage.parts.{{realIndex}}.base" value="{{dmg.base}}">
|
||||||
|
{{#unless dmg.base}}<div class="fas fa-trash" data-action="removeDamage" data-index="{{realIndex}}"></div>{{/unless}}
|
||||||
|
</fieldset>
|
||||||
|
{{/with}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
19
templates/views/actionTypes/effect.hbs
Normal file
19
templates/views/actionTypes/effect.hbs
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="effects">
|
||||||
|
<div>Effects</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open" data-key="effects">
|
||||||
|
<div class="fas fa-plus icon-button" data-action="addEffect"></div>
|
||||||
|
{{#each @root.effects as | effect index | }}
|
||||||
|
<fieldset>
|
||||||
|
{{!-- <div class="multi-display"> --}}
|
||||||
|
<div class="form-group">
|
||||||
|
<img src="{{img}}">
|
||||||
|
<label data-action="editEffect">{{name}}</label>
|
||||||
|
<div class="fas fa-trash" data-action="removeEffect" data-index="{{index}}"></div>
|
||||||
|
</div>
|
||||||
|
{{!-- </div> --}}
|
||||||
|
</fieldset>
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
21
templates/views/actionTypes/healing.hbs
Normal file
21
templates/views/actionTypes/healing.hbs
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="effects">
|
||||||
|
<div>Healing</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
<fieldset>
|
||||||
|
{{formField fields.type value=source.type name="healing.type" localize=true}}
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField fields.value.fields.custom.fields.enabled value=source.value.custom.enabled name="healing.value.custom.enabled"}}
|
||||||
|
{{#if source.value.custom.enabled}}
|
||||||
|
{{formField fields.value.fields.custom.fields.formula value=source.value.custom.formula name="healing.value.custom.formula" localize=true}}
|
||||||
|
{{else}}
|
||||||
|
{{formField fields.value.fields.multiplier value=source.value.multiplier name="healing.value.multiplier" localize=true}}
|
||||||
|
{{formField fields.value.fields.dice value=source.value.dice name="healing.value.dice"}}
|
||||||
|
{{formField fields.value.fields.bonus value=source.value.bonus name="healing.value.bonus" localize=true}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
13
templates/views/actionTypes/range-target.hbs
Normal file
13
templates/views/actionTypes/range-target.hbs
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="range">
|
||||||
|
<div>Range{{#if fields.target}} & Target{{/if}}</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formField fields.range value=source.range label="Range" name="range" localize=true}}
|
||||||
|
</div>
|
||||||
|
{{#if fields.target}}
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formField fields.target.type value=source.target.type label="Target" name="target.type" localize=true}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</fieldset>
|
||||||
14
templates/views/actionTypes/resource.hbs
Normal file
14
templates/views/actionTypes/resource.hbs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="effects">
|
||||||
|
<div>Resource</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
<fieldset>
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField fields.type value=source.type name="resource.type" localize=true}}
|
||||||
|
{{formField fields.value value=source.value name="resource.value"}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
10
templates/views/actionTypes/roll.hbs
Normal file
10
templates/views/actionTypes/roll.hbs
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="roll">
|
||||||
|
<div>Roll</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formField fields.type label="Type" name="roll.type" value=source.type localize=true}}
|
||||||
|
{{formField fields.trait label="Trait" name="roll.trait" value=source.trait localize=true}}
|
||||||
|
{{formField fields.difficulty label="Difficulty" name="roll.difficulty" value=source.difficulty}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
8
templates/views/actionTypes/target.hbs
Normal file
8
templates/views/actionTypes/target.hbs
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="target">
|
||||||
|
<div>Target</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formField targetField.type label="Target" name="target" rootId=partId localize=true}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
12
templates/views/actionTypes/uses.hbs
Normal file
12
templates/views/actionTypes/uses.hbs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="uses">
|
||||||
|
<div>Uses</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
<div class="multi-display">
|
||||||
|
{{formField fields.value label="Value" value=source.value name="uses.value" rootId=partId}}
|
||||||
|
{{formField fields.max label="Max" value=source.max name="uses.max" rootId=partId}}
|
||||||
|
</div>
|
||||||
|
{{formField fields.recovery label="Recovery" value=source.recovery name="uses.recovery" rootId=partId localize=true}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
9
templates/views/actionTypes/uuid.hbs
Normal file
9
templates/views/actionTypes/uuid.hbs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
<fieldset class="action-category">
|
||||||
|
<legend class="action-category-label" data-action="toggleSection" data-section="effects">
|
||||||
|
<div>Macro</div>
|
||||||
|
</legend>
|
||||||
|
<div class="action-category-data open">
|
||||||
|
{{formInput fields value=source name="documentUUID" placeholder=fields.options.placeholder}}
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<button class="disadvantage flex1 {{#if this.advantage}}selected{{/if}}" data-action="setAdvantage">Advantage</button>
|
<button class="disadvantage flex1 {{#if this.advantage}}selected{{/if}}" data-action="updateIsAdvantage" data-advantage="true">{{localize "DAGGERHEART.General.Advantage.Full"}}</button>
|
||||||
<button class="disadvantage flex1 {{#if this.disadvantage}}selected{{/if}}" data-action="setDisadvantage">Disadvantage</button>
|
<button class="disadvantage flex1 {{#if (eq this.advantage false)}}selected{{/if}}" data-action="updateIsAdvantage">{{localize "DAGGERHEART.General.Disadvantage.Full"}}</button>
|
||||||
</div>
|
</div>
|
||||||
{{#if (not this.isNpc)}}
|
{{#if (not this.isNpc)}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue