mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-15 13:11:08 +01:00
113 - Character Data Model (#114)
* Improved Character datamodel * Removed additional unneccessary getters * Preliminary cleanup in the class sheet * Cleanup of 'pc' references * Corrected Duality rolling from Character * Fix to damage roll
This commit is contained in:
parent
02f16f7363
commit
70382df63c
34 changed files with 1135 additions and 2001 deletions
|
|
@ -13,6 +13,7 @@ import { dualityRollEnricher } from './module/enrichers/DualityRollEnricher.mjs'
|
||||||
import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs';
|
import { getCommandTarget, rollCommandToJSON, setDiceSoNiceForDualityRoll } from './module/helpers/utils.mjs';
|
||||||
import { abilities } from './module/config/actorConfig.mjs';
|
import { abilities } from './module/config/actorConfig.mjs';
|
||||||
import Resources from './module/applications/resources.mjs';
|
import Resources from './module/applications/resources.mjs';
|
||||||
|
import DHDualityRoll from './module/data/chat-message/dualityRoll.mjs';
|
||||||
|
|
||||||
globalThis.SYSTEM = SYSTEM;
|
globalThis.SYSTEM = SYSTEM;
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ Hooks.once('init', () => {
|
||||||
CONFIG.Item.dataModels = models.items.config;
|
CONFIG.Item.dataModels = models.items.config;
|
||||||
|
|
||||||
const { Items, Actors } = foundry.documents.collections;
|
const { Items, Actors } = foundry.documents.collections;
|
||||||
Items.unregisterSheet('core', foundry.appv1.sheets.ItemSheet);
|
Items.unregisterSheet('core', foundry.applications.sheets.ItemSheetV2);
|
||||||
Items.registerSheet(SYSTEM.id, applications.DhpAncestry, { types: ['ancestry'], makeDefault: true });
|
Items.registerSheet(SYSTEM.id, applications.DhpAncestry, { types: ['ancestry'], makeDefault: true });
|
||||||
Items.registerSheet(SYSTEM.id, applications.DhpCommunity, { types: ['community'], makeDefault: true });
|
Items.registerSheet(SYSTEM.id, applications.DhpCommunity, { types: ['community'], makeDefault: true });
|
||||||
Items.registerSheet(SYSTEM.id, applications.DhpClassSheet, { types: ['class'], makeDefault: true });
|
Items.registerSheet(SYSTEM.id, applications.DhpClassSheet, { types: ['class'], makeDefault: true });
|
||||||
|
|
@ -54,12 +55,12 @@ Hooks.once('init', () => {
|
||||||
|
|
||||||
CONFIG.Actor.documentClass = documents.DhpActor;
|
CONFIG.Actor.documentClass = documents.DhpActor;
|
||||||
CONFIG.Actor.dataModels = {
|
CONFIG.Actor.dataModels = {
|
||||||
pc: models.DhpPC,
|
character: models.DhCharacter,
|
||||||
adversary: models.DhpAdversary,
|
adversary: models.DhpAdversary,
|
||||||
environment: models.DhpEnvironment
|
environment: models.DhpEnvironment
|
||||||
};
|
};
|
||||||
Actors.unregisterSheet('core', foundry.appv1.sheets.ActorSheet);
|
Actors.unregisterSheet('core', foundry.applications.sheets.ActorSheetV2);
|
||||||
Actors.registerSheet(SYSTEM.id, applications.DhpPCSheet, { types: ['pc'], makeDefault: true });
|
Actors.registerSheet(SYSTEM.id, applications.DhCharacterSheet, { types: ['character'], makeDefault: true });
|
||||||
Actors.registerSheet(SYSTEM.id, applications.DhpAdversarySheet, { types: ['adversary'], makeDefault: true });
|
Actors.registerSheet(SYSTEM.id, applications.DhpAdversarySheet, { types: ['adversary'], makeDefault: true });
|
||||||
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
Actors.registerSheet(SYSTEM.id, applications.DhpEnvironment, { types: ['environment'], makeDefault: true });
|
||||||
|
|
||||||
|
|
@ -137,22 +138,28 @@ const renderDualityButton = async event => {
|
||||||
title: button.dataset.label,
|
title: button.dataset.label,
|
||||||
value: rollModifier
|
value: rollModifier
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const systemData = new DHDualityRoll({
|
||||||
|
title: button.dataset.label,
|
||||||
|
origin: target.id,
|
||||||
|
roll: roll._formula,
|
||||||
|
modifiers: modifiers,
|
||||||
|
hope: hope,
|
||||||
|
fear: fear,
|
||||||
|
advantage: advantage,
|
||||||
|
disadvantage: disadvantage
|
||||||
|
});
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
const msgData = {
|
const msgData = {
|
||||||
type: 'dualityRoll',
|
type: 'dualityRoll',
|
||||||
sound: CONFIG.sounds.dice,
|
sound: CONFIG.sounds.dice,
|
||||||
system: {
|
system: systemData,
|
||||||
title: button.dataset.label,
|
|
||||||
origin: target.id,
|
|
||||||
roll: roll._formula,
|
|
||||||
modifiers: modifiers,
|
|
||||||
hope: hope,
|
|
||||||
fear: fear,
|
|
||||||
advantage: advantage,
|
|
||||||
disadvantage: disadvantage
|
|
||||||
},
|
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||||
|
systemData
|
||||||
|
),
|
||||||
rolls: [roll]
|
rolls: [roll]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -226,29 +233,34 @@ Hooks.on('chatMessage', (_, message) => {
|
||||||
: undefined,
|
: undefined,
|
||||||
title
|
title
|
||||||
});
|
});
|
||||||
}).then(({ roll, attribute, title }) => {
|
}).then(async ({ roll, attribute, title }) => {
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = new DHDualityRoll({
|
||||||
|
title: title,
|
||||||
|
origin: target?.id,
|
||||||
|
roll: roll._formula,
|
||||||
|
modifiers: attribute ? [attribute] : [],
|
||||||
|
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
|
||||||
|
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
|
||||||
|
advantage:
|
||||||
|
rollCommand.advantage && !rollCommand.disadvantage
|
||||||
|
? { 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 = {
|
||||||
type: 'dualityRoll',
|
type: 'dualityRoll',
|
||||||
sound: CONFIG.sounds.dice,
|
sound: CONFIG.sounds.dice,
|
||||||
system: {
|
system: systemData,
|
||||||
title: title,
|
|
||||||
origin: target?.id,
|
|
||||||
roll: roll._formula,
|
|
||||||
modifiers: attribute ? [attribute] : [],
|
|
||||||
hope: { dice: rollCommand.hope ?? 'd12', value: roll.dice[0].total },
|
|
||||||
fear: { dice: rollCommand.fear ?? 'd12', value: roll.dice[1].total },
|
|
||||||
advantage:
|
|
||||||
rollCommand.advantage && !rollCommand.disadvantage
|
|
||||||
? { dice: 'd6', value: roll.dice[2].total }
|
|
||||||
: undefined,
|
|
||||||
disadvantage:
|
|
||||||
rollCommand.disadvantage && !rollCommand.advantage
|
|
||||||
? { dice: 'd6', value: roll.dice[2].total }
|
|
||||||
: undefined
|
|
||||||
},
|
|
||||||
user: game.user.id,
|
user: game.user.id,
|
||||||
content: 'systems/daggerheart/templates/chat/duality-roll.hbs',
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||||
|
systemData
|
||||||
|
),
|
||||||
rolls: [roll]
|
rolls: [roll]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -275,10 +287,10 @@ const preloadHandlebarsTemplates = async function () {
|
||||||
'systems/daggerheart/templates/sheets/parts/heritage.hbs',
|
'systems/daggerheart/templates/sheets/parts/heritage.hbs',
|
||||||
'systems/daggerheart/templates/sheets/parts/subclassFeature.hbs',
|
'systems/daggerheart/templates/sheets/parts/subclassFeature.hbs',
|
||||||
'systems/daggerheart/templates/sheets/parts/effects.hbs',
|
'systems/daggerheart/templates/sheets/parts/effects.hbs',
|
||||||
'systems/daggerheart/templates/sheets/pc/sections/inventory.hbs',
|
'systems/daggerheart/templates/sheets/character/sections/inventory.hbs',
|
||||||
'systems/daggerheart/templates/sheets/pc/sections/loadout.hbs',
|
'systems/daggerheart/templates/sheets/character/sections/loadout.hbs',
|
||||||
'systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs',
|
'systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs',
|
||||||
'systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs',
|
'systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs',
|
||||||
'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',
|
||||||
|
|
|
||||||
22
lang/en.json
22
lang/en.json
|
|
@ -13,7 +13,7 @@
|
||||||
"armor": "Armor"
|
"armor": "Armor"
|
||||||
},
|
},
|
||||||
"Actor": {
|
"Actor": {
|
||||||
"pc": "PC",
|
"character": "Character",
|
||||||
"npc": "NPC",
|
"npc": "NPC",
|
||||||
"adversary": "Adversary",
|
"adversary": "Adversary",
|
||||||
"environment": "Environment"
|
"environment": "Environment"
|
||||||
|
|
@ -110,8 +110,14 @@
|
||||||
},
|
},
|
||||||
"VariantRules": {
|
"VariantRules": {
|
||||||
"ActionTokens": {
|
"ActionTokens": {
|
||||||
"Name": "Action Tokens",
|
"label": "Action Tokens",
|
||||||
"Hint": "Give each player action tokens to use in combat"
|
"hint": "Give each player action tokens to use in combat"
|
||||||
|
},
|
||||||
|
"FIELDS": {
|
||||||
|
"useCoins": {
|
||||||
|
"label": "Use Coins",
|
||||||
|
"hint": "test"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"DualityRollColor": {
|
"DualityRollColor": {
|
||||||
|
|
@ -132,10 +138,6 @@
|
||||||
"AttackTargetDoesNotExist": "The target token no longer exists"
|
"AttackTargetDoesNotExist": "The target token no longer exists"
|
||||||
},
|
},
|
||||||
"Error": {
|
"Error": {
|
||||||
"NoClassSelected": "Your character has no class selected!",
|
|
||||||
"LacksDomain": "Your character doesn't have the domain of the card!",
|
|
||||||
"MaxLoadoutReached": "You can't have any more domain cards at this level!",
|
|
||||||
"DuplicateDomainCard": "You already have a domain card with that name!",
|
|
||||||
"ActionRequiresTarget": "The action requires at least one target",
|
"ActionRequiresTarget": "The action requires at least one target",
|
||||||
"NoAssignedPlayerCharacter": "You have no assigned character.",
|
"NoAssignedPlayerCharacter": "You have no assigned character.",
|
||||||
"NoSelectedToken": "You have no selected token",
|
"NoSelectedToken": "You have no selected token",
|
||||||
|
|
@ -1252,7 +1254,11 @@
|
||||||
"MissingClass": "The character is missing a class",
|
"MissingClass": "The character is missing a class",
|
||||||
"SubclassNotInClass": "The subclass does not belong to the character's class",
|
"SubclassNotInClass": "The subclass does not belong to the character's class",
|
||||||
"ClassAlreadySelected": "The character already has a class",
|
"ClassAlreadySelected": "The character already has a class",
|
||||||
"SubclassAlreadySelected": "The character already has a subclass for that class."
|
"SubclassAlreadySelected": "The character already has a subclass for that class.",
|
||||||
|
"NoClassSelected": "Your character has no class selected!",
|
||||||
|
"LacksDomain": "Your character doesn't have the domain of the card!",
|
||||||
|
"MaxLoadoutReached": "You can't have any more domain cards at this level!",
|
||||||
|
"DuplicateDomainCard": "You already have a domain card with that name!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Effects": {
|
"Effects": {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export { default as DhpPCSheet } from './sheets/pc.mjs';
|
export { default as DhCharacterSheet } from './sheets/character.mjs';
|
||||||
export { default as DhpAdversarySheet } from './sheets/adversary.mjs';
|
export { default as DhpAdversarySheet } from './sheets/adversary.mjs';
|
||||||
export { default as DhpClassSheet } from './sheets/items/class.mjs';
|
export { default as DhpClassSheet } from './sheets/items/class.mjs';
|
||||||
export { default as DhpSubclass } from './sheets/items/subclass.mjs';
|
export { default as DhpSubclass } from './sheets/items/subclass.mjs';
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,10 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
const experienceIncreaseValues = experienceIncreases
|
const experienceIncreaseValues = experienceIncreases
|
||||||
.filter(exp => exp.data.length > 0)
|
.filter(exp => exp.data.length > 0)
|
||||||
.flatMap(exp =>
|
.flatMap(exp =>
|
||||||
exp.data.map(data => this.actor.system.experiences.find(x => x.id === data).description)
|
exp.data.map(data => {
|
||||||
|
const experience = Object.keys(this.actor.system.experiences).find(x => x === data);
|
||||||
|
return this.actor.system.experiences[experience].description;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
context.experienceIncreases = {
|
context.experienceIncreases = {
|
||||||
values: experienceIncreaseValues,
|
values: experienceIncreaseValues,
|
||||||
|
|
@ -201,7 +204,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
|
|
||||||
const multiclassSubclass = this.actor.system.multiclass?.system?.subclasses?.[0];
|
const multiclassSubclass = this.actor.system.multiclass?.system?.subclasses?.[0];
|
||||||
const possibleSubclasses = [
|
const possibleSubclasses = [
|
||||||
this.actor.system.subclass,
|
this.actor.system.class.subclass,
|
||||||
...(multiclassSubclass ? [multiclassSubclass] : [])
|
...(multiclassSubclass ? [multiclassSubclass] : [])
|
||||||
];
|
];
|
||||||
const selectedSubclasses = possibleSubclasses.filter(x => subclassSelections.includes(x.uuid));
|
const selectedSubclasses = possibleSubclasses.filter(x => subclassSelections.includes(x.uuid));
|
||||||
|
|
@ -274,8 +277,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
|
|
||||||
context.achievements = {
|
context.achievements = {
|
||||||
proficiency: {
|
proficiency: {
|
||||||
old: this.actor.system.proficiency.value,
|
old: this.actor.system.proficiency,
|
||||||
new: this.actor.system.proficiency.value + achivementProficiency,
|
new: this.actor.system.proficiency + achivementProficiency,
|
||||||
shown: achivementProficiency > 0
|
shown: achivementProficiency > 0
|
||||||
},
|
},
|
||||||
damageThresholds: {
|
damageThresholds: {
|
||||||
|
|
@ -328,10 +331,12 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
break;
|
break;
|
||||||
case 'experience':
|
case 'experience':
|
||||||
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
if (!advancement[choiceKey]) advancement[choiceKey] = [];
|
||||||
const data = checkbox.data.map(
|
const data = checkbox.data.map(data => {
|
||||||
data =>
|
const experience = Object.keys(this.actor.system.experiences).find(
|
||||||
this.actor.system.experiences.find(x => x.id === data)?.description ?? ''
|
x => x === data
|
||||||
);
|
);
|
||||||
|
return this.actor.system.experiences[experience]?.description ?? '';
|
||||||
|
});
|
||||||
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
advancement[choiceKey].push({ data: data, value: checkbox.value });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -354,8 +359,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
new: this.actor.system.resources.stress.max + (advancement.stress ?? 0)
|
new: this.actor.system.resources.stress.max + (advancement.stress ?? 0)
|
||||||
},
|
},
|
||||||
evasion: {
|
evasion: {
|
||||||
old: this.actor.system.evasion.value,
|
old: this.actor.system.evasion,
|
||||||
new: this.actor.system.evasion.value + (advancement.evasion ?? 0)
|
new: this.actor.system.evasion + (advancement.evasion ?? 0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
traits:
|
traits:
|
||||||
|
|
@ -421,8 +426,9 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
if (experienceIncreaseTagify) {
|
if (experienceIncreaseTagify) {
|
||||||
tagifyElement(
|
tagifyElement(
|
||||||
experienceIncreaseTagify,
|
experienceIncreaseTagify,
|
||||||
this.actor.system.experiences.reduce((acc, experience) => {
|
Object.keys(this.actor.system.experiences).reduce((acc, id) => {
|
||||||
acc[experience.id] = { label: experience.description };
|
const experience = this.actor.system.experiences[id];
|
||||||
|
acc[id] = { label: experience.description };
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {}),
|
||||||
|
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
|
||||||
|
|
||||||
export default class DhpMulticlassDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
|
||||||
constructor(actorName, actorClass, resolve) {
|
|
||||||
super({});
|
|
||||||
|
|
||||||
this.actorName = actorName;
|
|
||||||
this.actorClass = actorClass;
|
|
||||||
this.resolve = resolve;
|
|
||||||
|
|
||||||
this.classChoices = Array.from(
|
|
||||||
game.items.reduce((acc, x) => {
|
|
||||||
if (x.type === 'class' && x.name !== actorClass.name) {
|
|
||||||
acc.add(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, new Set())
|
|
||||||
);
|
|
||||||
this.subclassChoices = [];
|
|
||||||
this.domainChoices = [];
|
|
||||||
|
|
||||||
this.data = {
|
|
||||||
class: null,
|
|
||||||
subclass: null,
|
|
||||||
domain: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get title() {
|
|
||||||
return `${this.actorName} - Multiclass`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
|
||||||
classes: ['daggerheart', 'views', 'multiclass'],
|
|
||||||
position: { width: 600, height: 'auto' },
|
|
||||||
actions: {
|
|
||||||
selectClass: this.selectClass,
|
|
||||||
selectSubclass: this.selectSubclass,
|
|
||||||
selectDomain: this.selectDomain,
|
|
||||||
finish: this.finish
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static PARTS = {
|
|
||||||
form: {
|
|
||||||
id: 'levelup',
|
|
||||||
template: 'systems/daggerheart/templates/views/multiclass.hbs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async _prepareContext(_options) {
|
|
||||||
const context = await super._prepareContext(_options);
|
|
||||||
context.classChoices = this.classChoices;
|
|
||||||
context.subclassChoices = this.subclassChoices;
|
|
||||||
context.domainChoices = this.domainChoices;
|
|
||||||
context.disabledFinish = !this.data.class || !this.data.subclass || !this.data.domain;
|
|
||||||
context.data = this.data;
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async selectClass(_, button) {
|
|
||||||
const oldClass = this.data.class;
|
|
||||||
this.data.class = this.data.class?.uuid === button.dataset.class ? null : await fromUuid(button.dataset.class);
|
|
||||||
if (oldClass !== button.dataset.class) {
|
|
||||||
this.data.subclass = null;
|
|
||||||
this.data.domain = null;
|
|
||||||
this.subclassChoices = this.data.class ? this.data.class.system.subclasses : [];
|
|
||||||
|
|
||||||
//FIXME
|
|
||||||
this.domainChoices = this.data.class
|
|
||||||
? this.data.class.system.domains.map(x => {
|
|
||||||
const config = SYSTEM.DOMAIN.domains[x];
|
|
||||||
return {
|
|
||||||
name: game.i18n.localize(config.name),
|
|
||||||
id: config.id,
|
|
||||||
img: config.src,
|
|
||||||
disabled: this.actorClass.system.domains.includes(config.id)
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
this.render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async selectSubclass(_, button) {
|
|
||||||
this.data.subclass =
|
|
||||||
this.data.subclass?.uuid === button.dataset.subclass
|
|
||||||
? null
|
|
||||||
: this.subclassChoices.find(x => x.uuid === button.dataset.subclass);
|
|
||||||
this.render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async selectDomain(_, button) {
|
|
||||||
const domain =
|
|
||||||
this.data.domain?.id === button.dataset.domain
|
|
||||||
? null
|
|
||||||
: this.domainChoices.find(x => x.id === button.dataset.domain);
|
|
||||||
if (domain?.disabled) return;
|
|
||||||
|
|
||||||
this.data.domain = domain;
|
|
||||||
this.render(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static finish() {
|
|
||||||
this.close({}, this.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
async close(options = {}, data = null) {
|
|
||||||
this.resolve(data);
|
|
||||||
super.close(options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
|
||||||
|
|
||||||
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
export default class RollSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
constructor(experiences, bonusDamage, hopeResource, resolve, isNpc) {
|
constructor(experiences, hopeResource, resolve) {
|
||||||
super({}, {});
|
super({}, {});
|
||||||
|
|
||||||
this.experiences = experiences;
|
this.experiences = experiences;
|
||||||
|
|
@ -17,23 +17,13 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
fear: ['d12'],
|
fear: ['d12'],
|
||||||
advantage: null,
|
advantage: null,
|
||||||
disadvantage: null,
|
disadvantage: null,
|
||||||
bonusDamage: bonusDamage.reduce((acc, x) => {
|
|
||||||
if (x.appliesOn === SYSTEM.EFFECTS.applyLocations.attackRoll.id) {
|
|
||||||
acc.push({
|
|
||||||
...x,
|
|
||||||
hopeUses: 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, []),
|
|
||||||
hopeResource: hopeResource
|
hopeResource: hopeResource
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
id: 'roll-selection', //Having an id causes a new instance to overwrite previous.
|
id: 'roll-selection',
|
||||||
classes: ['daggerheart', 'views', 'roll-selection'],
|
classes: ['daggerheart', 'views', 'roll-selection'],
|
||||||
position: {
|
position: {
|
||||||
width: 400,
|
width: 400,
|
||||||
|
|
@ -41,8 +31,6 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
selectExperience: this.selectExperience,
|
selectExperience: this.selectExperience,
|
||||||
decreaseHopeUse: this.decreaseHopeUse,
|
|
||||||
increaseHopeUse: this.increaseHopeUse,
|
|
||||||
setAdvantage: this.setAdvantage,
|
setAdvantage: this.setAdvantage,
|
||||||
setDisadvantage: this.setDisadvantage,
|
setDisadvantage: this.setDisadvantage,
|
||||||
finish: this.finish
|
finish: this.finish
|
||||||
|
|
@ -74,27 +62,14 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
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.disadvantage = this.data.disadvantage;
|
||||||
context.experiences = this.experiences.map(x => ({
|
context.experiences = Object.keys(this.experiences).map(id => ({ id, ...this.experiences[id] }));
|
||||||
...x,
|
|
||||||
selected: this.selectedExperiences.find(selected => selected.id === x.id)
|
|
||||||
}));
|
|
||||||
context.bonusDamage = this.data.bonusDamage;
|
|
||||||
context.hopeResource = this.data.hopeResource + 1;
|
context.hopeResource = this.data.hopeResource + 1;
|
||||||
context.hopeUsed = this.getHopeUsed();
|
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static updateSelection(event, _, formData) {
|
static updateSelection(event, _, formData) {
|
||||||
const { bonusDamage, ...rest } = foundry.utils.expandObject(formData.object);
|
const { ...rest } = foundry.utils.expandObject(formData.object);
|
||||||
|
|
||||||
for (var index in bonusDamage) {
|
|
||||||
this.data.bonusDamage[index].initiallySelected = bonusDamage[index].initiallySelected;
|
|
||||||
if (bonusDamage[index].hopeUses) {
|
|
||||||
const value = Number.parseInt(bonusDamage[index].hopeUses);
|
|
||||||
if (!Number.isNaN(value)) this.data.bonusDamage[index].hopeUses = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data = foundry.utils.mergeObject(this.data, rest);
|
this.data = foundry.utils.mergeObject(this.data, rest);
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -104,35 +79,12 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
if (this.selectedExperiences.find(x => x.id === button.dataset.key)) {
|
if (this.selectedExperiences.find(x => x.id === button.dataset.key)) {
|
||||||
this.selectedExperiences = this.selectedExperiences.filter(x => x.id !== button.dataset.key);
|
this.selectedExperiences = this.selectedExperiences.filter(x => x.id !== button.dataset.key);
|
||||||
} else {
|
} else {
|
||||||
this.selectedExperiences = [
|
this.selectedExperiences = [...this.selectedExperiences, button.dataset.key];
|
||||||
...this.selectedExperiences,
|
|
||||||
this.experiences.find(x => x.id === button.dataset.key)
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
getHopeUsed() {
|
|
||||||
return this.data.bonusDamage.reduce((acc, x) => acc + x.hopeUses, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static decreaseHopeUse(_, button) {
|
|
||||||
const index = Number.parseInt(button.dataset.index);
|
|
||||||
if (this.data.bonusDamage[index].hopeUses - 1 >= 0) {
|
|
||||||
this.data.bonusDamage[index].hopeUses -= 1;
|
|
||||||
this.render(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static increaseHopeUse(_, button) {
|
|
||||||
const index = Number.parseInt(button.dataset.index);
|
|
||||||
if (this.data.bonusDamage[index].hopeUses <= this.data.hopeResource + 1) {
|
|
||||||
this.data.bonusDamage[index].hopeUses += 1;
|
|
||||||
this.render(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static setAdvantage() {
|
static setAdvantage() {
|
||||||
this.data.advantage = this.data.advantage ? null : 'd6';
|
this.data.advantage = this.data.advantage ? null : 'd6';
|
||||||
this.data.disadvantage = null;
|
this.data.disadvantage = null;
|
||||||
|
|
@ -149,11 +101,10 @@ export default class RollSelectionDialog extends HandlebarsApplicationMixin(Appl
|
||||||
|
|
||||||
static async finish() {
|
static async finish() {
|
||||||
const { diceOptions, ...rest } = this.data;
|
const { diceOptions, ...rest } = this.data;
|
||||||
|
|
||||||
this.resolve({
|
this.resolve({
|
||||||
...rest,
|
...rest,
|
||||||
experiences: this.selectedExperiences,
|
experiences: this.selectedExperiences.map(x => ({ id: x, ...this.experiences[x] }))
|
||||||
hopeUsed: this.getHopeUsed(),
|
|
||||||
bonusDamage: this.data.bonusDamage.reduce((acc, x) => acc.concat(` + ${1 + x.hopeUses}${x.value}`), '')
|
|
||||||
});
|
});
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ export default class AdversarySheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
name: x.actor.name,
|
name: x.actor.name,
|
||||||
img: x.actor.img,
|
img: x.actor.img,
|
||||||
difficulty: x.actor.system.difficulty,
|
difficulty: x.actor.system.difficulty,
|
||||||
evasion: x.actor.system.evasion.value
|
evasion: x.actor.system.evasion
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const cls = getDocumentClass('ChatMessage');
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
|
|
||||||
703
module/applications/sheets/character.mjs
Normal file
703
module/applications/sheets/character.mjs
Normal file
|
|
@ -0,0 +1,703 @@
|
||||||
|
import { capitalize } from '../../helpers/utils.mjs';
|
||||||
|
import DhpDeathMove from '../deathMove.mjs';
|
||||||
|
import DhpDowntime from '../downtime.mjs';
|
||||||
|
import AncestrySelectionDialog from '../ancestrySelectionDialog.mjs';
|
||||||
|
import DaggerheartSheet from './daggerheart-sheet.mjs';
|
||||||
|
import { abilities } from '../../config/actorConfig.mjs';
|
||||||
|
import DhlevelUp from '../levelup.mjs';
|
||||||
|
import DHDualityRoll from '../../data/chat-message/dualityRoll.mjs';
|
||||||
|
|
||||||
|
const { ActorSheetV2 } = foundry.applications.sheets;
|
||||||
|
const { TextEditor } = foundry.applications.ux;
|
||||||
|
export default class CharacterSheet extends DaggerheartSheet(ActorSheetV2) {
|
||||||
|
constructor(options = {}) {
|
||||||
|
super(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static DEFAULT_OPTIONS = {
|
||||||
|
tag: 'form',
|
||||||
|
classes: ['daggerheart', 'sheet', 'pc'],
|
||||||
|
position: { width: 810, height: 1080 },
|
||||||
|
actions: {
|
||||||
|
attributeRoll: this.rollAttribute,
|
||||||
|
toggleMarks: this.toggleMarks,
|
||||||
|
toggleHP: this.toggleHP,
|
||||||
|
toggleStress: this.toggleStress,
|
||||||
|
toggleHope: this.toggleHope,
|
||||||
|
toggleGold: this.toggleGold,
|
||||||
|
attackRoll: this.attackRoll,
|
||||||
|
useDomainCard: this.useDomainCard,
|
||||||
|
removeCard: this.removeDomainCard,
|
||||||
|
selectClass: this.selectClass,
|
||||||
|
selectSubclass: this.selectSubclass,
|
||||||
|
selectAncestry: this.selectAncestry,
|
||||||
|
selectCommunity: this.selectCommunity,
|
||||||
|
viewObject: this.viewObject,
|
||||||
|
useFeature: this.useFeature,
|
||||||
|
takeShortRest: this.takeShortRest,
|
||||||
|
takeLongRest: this.takeLongRest,
|
||||||
|
deleteItem: this.deleteItem,
|
||||||
|
addScar: this.addScar,
|
||||||
|
deleteScar: this.deleteScar,
|
||||||
|
makeDeathMove: this.makeDeathMove,
|
||||||
|
itemQuantityDecrease: (_, button) => this.setItemQuantity(button, -1),
|
||||||
|
itemQuantityIncrease: (_, button) => this.setItemQuantity(button, 1),
|
||||||
|
useAbility: this.useAbility,
|
||||||
|
useAdvancementCard: this.useAdvancementCard,
|
||||||
|
useAdvancementAbility: this.useAdvancementAbility,
|
||||||
|
toggleEquipItem: this.toggleEquipItem,
|
||||||
|
levelup: this.openLevelUp
|
||||||
|
},
|
||||||
|
window: {
|
||||||
|
minimizable: false,
|
||||||
|
resizable: true
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
handler: this.updateForm,
|
||||||
|
submitOnChange: true,
|
||||||
|
closeOnSubmit: false
|
||||||
|
},
|
||||||
|
dragDrop: [
|
||||||
|
{ dragSelector: null, dropSelector: '.weapon-section' },
|
||||||
|
{ dragSelector: null, dropSelector: '.armor-section' },
|
||||||
|
{ dragSelector: '.item-list .item', dropSelector: null }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
static PARTS = {
|
||||||
|
form: {
|
||||||
|
id: 'character',
|
||||||
|
template: 'systems/daggerheart/templates/sheets/character/character.hbs'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_getTabs() {
|
||||||
|
const setActive = tabs => {
|
||||||
|
for (const v of Object.values(tabs)) {
|
||||||
|
v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active;
|
||||||
|
v.cssClass = v.active ? 'active' : '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const primaryTabs = {
|
||||||
|
features: {
|
||||||
|
active: true,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'features',
|
||||||
|
icon: null,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Features')
|
||||||
|
},
|
||||||
|
loadout: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'loadout',
|
||||||
|
icon: null,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Loadout')
|
||||||
|
},
|
||||||
|
inventory: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'inventory',
|
||||||
|
icon: null,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Inventory')
|
||||||
|
},
|
||||||
|
story: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'primary',
|
||||||
|
id: 'story',
|
||||||
|
icon: null,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Story')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const secondaryTabs = {
|
||||||
|
foundation: {
|
||||||
|
active: true,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'secondary',
|
||||||
|
id: 'foundation',
|
||||||
|
icon: null,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Foundation')
|
||||||
|
},
|
||||||
|
loadout: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'secondary',
|
||||||
|
id: 'loadout',
|
||||||
|
icon: null,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Loadout')
|
||||||
|
},
|
||||||
|
vault: {
|
||||||
|
active: false,
|
||||||
|
cssClass: '',
|
||||||
|
group: 'secondary',
|
||||||
|
id: 'vault',
|
||||||
|
icon: null,
|
||||||
|
label: game.i18n.localize('DAGGERHEART.Sheets.PC.Tabs.Vault')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setActive(primaryTabs);
|
||||||
|
setActive(secondaryTabs);
|
||||||
|
|
||||||
|
return { primary: primaryTabs, secondary: secondaryTabs };
|
||||||
|
}
|
||||||
|
|
||||||
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
|
||||||
|
htmlElement.querySelector('.level-value').addEventListener('change', this.onLevelChange.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async _prepareContext(_options) {
|
||||||
|
const context = await super._prepareContext(_options);
|
||||||
|
context.document = this.document;
|
||||||
|
context.tabs = this._getTabs();
|
||||||
|
|
||||||
|
context.config = SYSTEM;
|
||||||
|
|
||||||
|
const selectedAttributes = Object.values(this.document.system.traits).map(x => x.base);
|
||||||
|
context.abilityScoreArray = JSON.parse(
|
||||||
|
await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.General.AbilityArray)
|
||||||
|
).reduce((acc, x) => {
|
||||||
|
const selectedIndex = selectedAttributes.indexOf(x);
|
||||||
|
if (selectedIndex !== -1) {
|
||||||
|
selectedAttributes.splice(selectedIndex, 1);
|
||||||
|
} else {
|
||||||
|
acc.push({ name: x, value: x });
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
if (!context.abilityScoreArray.includes(0)) context.abilityScoreArray.push({ name: 0, value: 0 });
|
||||||
|
context.abilityScoresFinished = context.abilityScoreArray.every(x => x.value === 0);
|
||||||
|
|
||||||
|
context.attributes = Object.keys(this.document.system.traits).reduce((acc, key) => {
|
||||||
|
acc[key] = {
|
||||||
|
...this.document.system.traits[key],
|
||||||
|
name: game.i18n.localize(SYSTEM.ACTOR.abilities[key].name),
|
||||||
|
verbs: SYSTEM.ACTOR.abilities[key].verbs.map(x => game.i18n.localize(x))
|
||||||
|
};
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const ancestry = await this.mapFeatureType(
|
||||||
|
this.document.system.ancestry ? [this.document.system.ancestry] : [],
|
||||||
|
SYSTEM.GENERAL.objectTypes
|
||||||
|
);
|
||||||
|
const community = await this.mapFeatureType(
|
||||||
|
this.document.system.community ? [this.document.system.community] : [],
|
||||||
|
SYSTEM.GENERAL.objectTypes
|
||||||
|
);
|
||||||
|
const foundation = {
|
||||||
|
ancestry: ancestry[0],
|
||||||
|
community: community[0],
|
||||||
|
advancement: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nrLoadoutCards = this.document.system.domainCards.loadout.length;
|
||||||
|
const loadout = await this.mapFeatureType(this.document.system.domainCards.loadout, SYSTEM.DOMAIN.cardTypes);
|
||||||
|
const vault = await this.mapFeatureType(this.document.system.domainCards.vault, SYSTEM.DOMAIN.cardTypes);
|
||||||
|
context.abilities = {
|
||||||
|
foundation: foundation,
|
||||||
|
loadout: {
|
||||||
|
top: loadout.slice(0, Math.min(2, nrLoadoutCards)),
|
||||||
|
bottom: nrLoadoutCards > 2 ? loadout.slice(2, Math.min(5, nrLoadoutCards)) : [],
|
||||||
|
nrTotal: nrLoadoutCards
|
||||||
|
},
|
||||||
|
vault: vault.map(x => ({
|
||||||
|
...x,
|
||||||
|
uuid: x.uuid,
|
||||||
|
sendToLoadoutDisabled: this.document.system.domainCards.loadout.length >= 5
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
|
context.inventory = {
|
||||||
|
consumable: {
|
||||||
|
titles: {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ConsumableTitle'),
|
||||||
|
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||||
|
},
|
||||||
|
items: this.document.items.filter(x => x.type === 'consumable')
|
||||||
|
},
|
||||||
|
miscellaneous: {
|
||||||
|
titles: {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.MiscellaneousTitle'),
|
||||||
|
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||||
|
},
|
||||||
|
items: this.document.items.filter(x => x.type === 'miscellaneous')
|
||||||
|
},
|
||||||
|
weapons: {
|
||||||
|
titles: {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.WeaponsTitle'),
|
||||||
|
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||||
|
},
|
||||||
|
items: this.document.items.filter(x => x.type === 'weapon')
|
||||||
|
},
|
||||||
|
armor: {
|
||||||
|
titles: {
|
||||||
|
name: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.ArmorsTitle'),
|
||||||
|
quantity: game.i18n.localize('DAGGERHEART.Sheets.PC.InventoryTab.QuantityTitle')
|
||||||
|
},
|
||||||
|
items: this.document.items.filter(x => x.type === 'armor')
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (context.inventory.length === 0) {
|
||||||
|
context.inventory = Array(1).fill(Array(5).fill([]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async updateForm(event, _, formData) {
|
||||||
|
await this.document.update(formData.object);
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async mapFeatureType(data, configType) {
|
||||||
|
return await Promise.all(
|
||||||
|
data.map(async x => {
|
||||||
|
const abilities = x.system.abilities
|
||||||
|
? await Promise.all(x.system.abilities.map(async x => await fromUuid(x.uuid)))
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...x,
|
||||||
|
uuid: x.uuid,
|
||||||
|
system: {
|
||||||
|
...x.system,
|
||||||
|
abilities: abilities,
|
||||||
|
type: game.i18n.localize(configType[x.system.type ?? x.type].label)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async rollAttribute(event, button) {
|
||||||
|
const { roll, hope, fear, advantage, disadvantage, modifiers } = await this.document.dualityRoll(
|
||||||
|
{ title: game.i18n.localize(abilities[button.dataset.attribute].label), value: button.dataset.value },
|
||||||
|
event.shiftKey
|
||||||
|
);
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
|
||||||
|
const systemContent = new DHDualityRoll({
|
||||||
|
title: game.i18n.format('DAGGERHEART.Chat.DualityRoll.AbilityCheckTitle', {
|
||||||
|
ability: game.i18n.localize(abilities[button.dataset.attribute].label)
|
||||||
|
}),
|
||||||
|
origin: this.document.id,
|
||||||
|
roll: roll._formula,
|
||||||
|
modifiers: modifiers,
|
||||||
|
hope: hope,
|
||||||
|
fear: fear,
|
||||||
|
advantage: advantage,
|
||||||
|
disadvantage: disadvantage
|
||||||
|
});
|
||||||
|
|
||||||
|
await cls.create({
|
||||||
|
type: 'dualityRoll',
|
||||||
|
sound: CONFIG.sounds.dice,
|
||||||
|
system: systemContent,
|
||||||
|
user: game.user.id,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/duality-roll.hbs',
|
||||||
|
systemContent
|
||||||
|
),
|
||||||
|
rolls: [roll]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toggleMarks(_, button) {
|
||||||
|
const markValue = Number.parseInt(button.dataset.value);
|
||||||
|
const newValue = this.document.system.armor.system.marks.value >= markValue ? markValue - 1 : markValue;
|
||||||
|
await this.document.system.armor.update({ 'system.marks.value': newValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toggleHP(_, button) {
|
||||||
|
const healthValue = Number.parseInt(button.dataset.value);
|
||||||
|
const newValue = this.document.system.resources.hitPoints.value >= healthValue ? healthValue - 1 : healthValue;
|
||||||
|
await this.document.update({ 'system.resources.hitPoints.value': newValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toggleStress(_, button) {
|
||||||
|
const healthValue = Number.parseInt(button.dataset.value);
|
||||||
|
const newValue = this.document.system.resources.stress.value >= healthValue ? healthValue - 1 : healthValue;
|
||||||
|
await this.document.update({ 'system.resources.stress.value': newValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toggleHope(_, button) {
|
||||||
|
const hopeValue = Number.parseInt(button.dataset.value);
|
||||||
|
const newValue = this.document.system.resources.hope.value >= hopeValue ? hopeValue - 1 : hopeValue;
|
||||||
|
await this.document.update({ 'system.resources.hope.value': newValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toggleGold(_, button) {
|
||||||
|
const goldValue = Number.parseInt(button.dataset.value);
|
||||||
|
const goldType = button.dataset.type;
|
||||||
|
const newValue = this.document.system.gold[goldType] >= goldValue ? goldValue - 1 : goldValue;
|
||||||
|
|
||||||
|
const update = `system.gold.${goldType}`;
|
||||||
|
await this.document.update({ [update]: newValue });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async attackRoll(event, button) {
|
||||||
|
const weapon = await fromUuid(button.dataset.weapon);
|
||||||
|
const damage = {
|
||||||
|
value: `${this.document.system.proficiency}${weapon.system.damage.value}`,
|
||||||
|
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() {
|
||||||
|
if (!this.document.system.class.value || !this.document.system.class.subclass) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Sheets.PC.Errors.missingClassOrSubclass'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new DhlevelUp(this.document).render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async useDomainCard(_, button) {
|
||||||
|
const card = this.document.items.find(x => x.uuid === button.dataset.key);
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = {
|
||||||
|
title: `${game.i18n.localize('DAGGERHEART.Chat.DomainCard.Title')} - ${capitalize(button.dataset.domain)}`,
|
||||||
|
origin: this.document.id,
|
||||||
|
img: card.img,
|
||||||
|
name: card.name,
|
||||||
|
description: card.system.effect,
|
||||||
|
actions: card.system.actions
|
||||||
|
};
|
||||||
|
const msg = new cls({
|
||||||
|
type: 'abilityUse',
|
||||||
|
user: game.user.id,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||||
|
systemData
|
||||||
|
),
|
||||||
|
system: systemData
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async removeDomainCard(_, button) {
|
||||||
|
if (button.dataset.type === 'domainCard') {
|
||||||
|
const card = this.document.items.find(x => x.uuid === button.dataset.key);
|
||||||
|
await card.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async selectClass() {
|
||||||
|
(await game.packs.get('daggerheart.classes'))?.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async selectSubclass() {
|
||||||
|
(await game.packs.get('daggerheart.subclasses'))?.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async selectAncestry() {
|
||||||
|
const dialogClosed = new Promise((resolve, _) => {
|
||||||
|
new AncestrySelectionDialog(resolve).render(true);
|
||||||
|
});
|
||||||
|
const result = await dialogClosed;
|
||||||
|
|
||||||
|
for (var ancestry of this.document.items.filter(x => x => x.type === 'ancestry')) {
|
||||||
|
await ancestry.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdItems = [];
|
||||||
|
for (var feature of this.document.items.filter(
|
||||||
|
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.ancestry.id
|
||||||
|
)) {
|
||||||
|
await feature.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
createdItems.push(result.data);
|
||||||
|
|
||||||
|
await this.document.createEmbeddedDocuments('Item', createdItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async selectCommunity() {
|
||||||
|
(await game.packs.get('daggerheart.communities'))?.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async viewObject(_, button) {
|
||||||
|
const object = await fromUuid(button.dataset.value);
|
||||||
|
if (!object) return;
|
||||||
|
|
||||||
|
const tab = button.dataset.tab;
|
||||||
|
if (tab && object.sheet._tabs) object.sheet._tabs[0].active = tab;
|
||||||
|
|
||||||
|
if (object.sheet.editMode) object.sheet.editMode = false;
|
||||||
|
|
||||||
|
object.sheet.render(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async takeShortRest() {
|
||||||
|
await new DhpDowntime(this.document, true).render(true);
|
||||||
|
await this.minimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async takeLongRest() {
|
||||||
|
await new DhpDowntime(this.document, false).render(true);
|
||||||
|
await this.minimize();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async addScar() {
|
||||||
|
if (this.document.system.story.scars.length === 5) return;
|
||||||
|
|
||||||
|
await this.document.update({
|
||||||
|
'system.story.scars': [
|
||||||
|
...this.document.system.story.scars,
|
||||||
|
{ name: game.i18n.localize('DAGGERHEART.Sheets.PC.NewScar'), description: '' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteScar(event, button) {
|
||||||
|
event.stopPropagation();
|
||||||
|
await this.document.update({
|
||||||
|
'system.story.scars': this.document.system.story.scars.filter(
|
||||||
|
(_, index) => index !== Number.parseInt(button.currentTarget.dataset.scar)
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static async makeDeathMove() {
|
||||||
|
if (this.document.system.resources.hitPoints.value === this.document.system.resources.hitPoints.max) {
|
||||||
|
await new DhpDeathMove(this.document).render(true);
|
||||||
|
await this.minimize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async itemUpdate(event) {
|
||||||
|
const name = event.currentTarget.dataset.item;
|
||||||
|
const item = await fromUuid($(event.currentTarget).closest('[data-item-id]')[0].dataset.itemId);
|
||||||
|
await item.update({ [name]: event.currentTarget.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
async onLevelChange(event) {
|
||||||
|
await this.document.updateLevel(Number(event.currentTarget.value));
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async deleteItem(_, button) {
|
||||||
|
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
|
||||||
|
await item.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async setItemQuantity(button, value) {
|
||||||
|
const item = await fromUuid($(button).closest('[data-item-id]')[0].dataset.itemId);
|
||||||
|
await item.update({ 'system.quantity': Math.max(item.system.quantity + value, 1) });
|
||||||
|
}
|
||||||
|
|
||||||
|
static async useFeature(_, button) {
|
||||||
|
const item = await fromUuid(button.dataset.id);
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.Chat.FeatureTitle'),
|
||||||
|
origin: this.document.id,
|
||||||
|
img: item.img,
|
||||||
|
name: item.name,
|
||||||
|
description: item.system.description,
|
||||||
|
actions: item.system.actions
|
||||||
|
};
|
||||||
|
const msg = new cls({
|
||||||
|
type: 'abilityUse',
|
||||||
|
user: game.user.id,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||||
|
systemData
|
||||||
|
),
|
||||||
|
system: systemData
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async useAbility(_, button) {
|
||||||
|
const item = await fromUuid(button.dataset.feature);
|
||||||
|
const type = button.dataset.type;
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = {
|
||||||
|
title:
|
||||||
|
type === 'ancestry'
|
||||||
|
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.AncestryTitle')
|
||||||
|
: type === 'community'
|
||||||
|
? game.i18n.localize('DAGGERHEART.Chat.FoundationCard.CommunityTitle')
|
||||||
|
: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
||||||
|
origin: this.document.id,
|
||||||
|
img: item.img,
|
||||||
|
name: item.name,
|
||||||
|
description: item.system.description,
|
||||||
|
actions: []
|
||||||
|
};
|
||||||
|
const msg = new cls({
|
||||||
|
type: 'abilityUse',
|
||||||
|
user: game.user.id,
|
||||||
|
system: systemData,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||||
|
systemData
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async useAdvancementCard(_, button) {
|
||||||
|
const item =
|
||||||
|
button.dataset.multiclass === 'true'
|
||||||
|
? this.document.system.multiclass.subclass
|
||||||
|
: this.document.system.class.subclass;
|
||||||
|
const ability = item.system[`${button.dataset.key}Feature`];
|
||||||
|
const title = `${item.name} - ${game.i18n.localize(`DAGGERHEART.Sheets.PC.DomainCard.${capitalize(button.dataset.key)}Title`)}`;
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
||||||
|
origin: this.document.id,
|
||||||
|
name: title,
|
||||||
|
img: item.img,
|
||||||
|
description: ability.description
|
||||||
|
};
|
||||||
|
const msg = new cls({
|
||||||
|
type: 'abilityUse',
|
||||||
|
user: game.user.id,
|
||||||
|
system: systemData,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||||
|
systemData
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async useAdvancementAbility(_, button) {
|
||||||
|
const item = this.document.items.find(x => x.uuid === button.dataset.id);
|
||||||
|
|
||||||
|
const cls = getDocumentClass('ChatMessage');
|
||||||
|
const systemData = {
|
||||||
|
title: game.i18n.localize('DAGGERHEART.Chat.FoundationCard.SubclassFeatureTitle'),
|
||||||
|
origin: this.document.id,
|
||||||
|
name: item.name,
|
||||||
|
img: item.img,
|
||||||
|
description: item.system.description
|
||||||
|
};
|
||||||
|
const msg = new cls({
|
||||||
|
user: game.user.id,
|
||||||
|
system: systemData,
|
||||||
|
content: await foundry.applications.handlebars.renderTemplate(
|
||||||
|
'systems/daggerheart/templates/chat/ability-use.hbs',
|
||||||
|
systemData
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
cls.create(msg.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
static async toggleEquipItem(_, button) {
|
||||||
|
const item = this.document.items.get(button.id);
|
||||||
|
if (item.system.equipped) {
|
||||||
|
await item.update({ 'system.equipped': false });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.type) {
|
||||||
|
case 'armor':
|
||||||
|
const currentArmor = this.document.system.armor;
|
||||||
|
if (currentArmor) {
|
||||||
|
await currentArmor.update({ 'system.equipped': false });
|
||||||
|
}
|
||||||
|
|
||||||
|
await item.update({ 'system.equipped': true });
|
||||||
|
break;
|
||||||
|
case 'weapon':
|
||||||
|
await this.document.system.constructor.unequipBeforeEquip.bind(this.document.system)(item);
|
||||||
|
|
||||||
|
await item.update({ 'system.equipped': true });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDragStart(_, event) {
|
||||||
|
super._onDragStart(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDrop(event) {
|
||||||
|
super._onDrop(event);
|
||||||
|
this._onDropItem(event, TextEditor.getDragEventData(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDropItem(event, data) {
|
||||||
|
const item = await Item.implementation.fromDropData(data);
|
||||||
|
const itemData = item.toObject();
|
||||||
|
|
||||||
|
if (item.type === 'domainCard' && this.document.system.domainCards.loadout.length >= 5) {
|
||||||
|
itemData.system.inVault = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.document.uuid === item.parent?.uuid) return this._onSortItem(event, itemData);
|
||||||
|
const createdItem = await this._onDropItemCreate(itemData);
|
||||||
|
|
||||||
|
return createdItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onDropItemCreate(itemData, event) {
|
||||||
|
itemData = itemData instanceof Array ? itemData : [itemData];
|
||||||
|
return this.document.createEmbeddedDocuments('Item', itemData);
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -76,7 +76,7 @@ export const featureProperties = {
|
||||||
},
|
},
|
||||||
spellcastingTrait: {
|
spellcastingTrait: {
|
||||||
name: 'DAGGERHEART.FeatureProperty.SpellcastingTrait',
|
name: 'DAGGERHEART.FeatureProperty.SpellcastingTrait',
|
||||||
path: actor => actor.system.traits[actor.system.subclass.system.spellcastingTrait].data.value
|
path: actor => actor.system.traits[actor.system.class.subclass.system.spellcastingTrait].data.value
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -199,8 +199,8 @@ export const tiers = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const objectTypes = {
|
export const objectTypes = {
|
||||||
pc: {
|
character: {
|
||||||
name: 'TYPES.Actor.pc'
|
name: 'TYPES.Actor.character'
|
||||||
},
|
},
|
||||||
npc: {
|
npc: {
|
||||||
name: 'TYPES.Actor.npc'
|
name: 'TYPES.Actor.npc'
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export { default as DhpPC } from './pc.mjs';
|
export { default as DhCharacter } from './character.mjs';
|
||||||
export { default as DhClass } from './item/class.mjs';
|
export { default as DhClass } from './item/class.mjs';
|
||||||
export { default as DhSubclass } from './item/subclass.mjs';
|
export { default as DhSubclass } from './item/subclass.mjs';
|
||||||
export { default as DhCombat } from './combat.mjs';
|
export { default as DhCombat } from './combat.mjs';
|
||||||
|
|
|
||||||
233
module/data/character.mjs
Normal file
233
module/data/character.mjs
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
import { burden } from '../config/generalConfig.mjs';
|
||||||
|
import ForeignDocumentUUIDField from './fields/foreignDocumentUUIDField.mjs';
|
||||||
|
import { LevelOptionType } from './levelTier.mjs';
|
||||||
|
|
||||||
|
const attributeField = () =>
|
||||||
|
new foundry.data.fields.SchemaField({
|
||||||
|
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
tierMarked: new foundry.data.fields.BooleanField({ initial: false })
|
||||||
|
});
|
||||||
|
|
||||||
|
const resourceField = max =>
|
||||||
|
new foundry.data.fields.SchemaField({
|
||||||
|
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
max: new foundry.data.fields.NumberField({ initial: max, integer: true })
|
||||||
|
});
|
||||||
|
|
||||||
|
export default class DhCharacter extends foundry.abstract.TypeDataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
resources: new fields.SchemaField({
|
||||||
|
hitPoints: resourceField(6),
|
||||||
|
stress: resourceField(6),
|
||||||
|
hope: resourceField(6)
|
||||||
|
}),
|
||||||
|
traits: new fields.SchemaField({
|
||||||
|
agility: attributeField(),
|
||||||
|
strength: attributeField(),
|
||||||
|
finesse: attributeField(),
|
||||||
|
instinct: attributeField(),
|
||||||
|
presence: attributeField(),
|
||||||
|
knowledge: attributeField()
|
||||||
|
}),
|
||||||
|
proficiency: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
|
evasion: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
experiences: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
description: new fields.StringField({}),
|
||||||
|
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
initial: {
|
||||||
|
[foundry.utils.randomID()]: { description: '', value: 2 },
|
||||||
|
[foundry.utils.randomID()]: { description: '', value: 2 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
gold: new fields.SchemaField({
|
||||||
|
coins: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
handfulls: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
bags: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
|
chests: new fields.NumberField({ initial: 0, integer: true })
|
||||||
|
}),
|
||||||
|
pronouns: new fields.StringField({}),
|
||||||
|
scars: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
name: new fields.StringField({}),
|
||||||
|
description: new fields.HTMLField()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
story: new fields.HTMLField(),
|
||||||
|
description: new fields.HTMLField(),
|
||||||
|
class: new fields.SchemaField({
|
||||||
|
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||||
|
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
||||||
|
}),
|
||||||
|
multiclass: new fields.SchemaField({
|
||||||
|
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||||
|
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
||||||
|
}),
|
||||||
|
levelData: new fields.EmbeddedDataField(DhPCLevelData)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get ancestry() {
|
||||||
|
return this.parent.items.find(x => x.type === 'ancestry') ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get community() {
|
||||||
|
return this.parent.items.find(x => x.type === 'community') ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get domains() {
|
||||||
|
const classDomains = this.class ? this.class.system.domains : [];
|
||||||
|
const multiclassDomains = this.multiclass ? this.multiclass.system.domains : [];
|
||||||
|
return [...classDomains, ...multiclassDomains];
|
||||||
|
}
|
||||||
|
|
||||||
|
get domainCards() {
|
||||||
|
const domainCards = this.parent.items.filter(x => x.type === 'domainCard');
|
||||||
|
const loadout = domainCards.filter(x => !x.system.inVault);
|
||||||
|
const vault = domainCards.filter(x => x.system.inVault);
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadout: loadout,
|
||||||
|
vault: vault,
|
||||||
|
total: [...loadout, ...vault]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get armor() {
|
||||||
|
return this.parent.items.find(x => x.type === 'armor' && x.system.equipped);
|
||||||
|
}
|
||||||
|
|
||||||
|
get primaryWeapon() {
|
||||||
|
return this.parent.items.find(x => x.type === 'weapon' && x.system.equipped && !x.system.secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
get secondaryWeapon() {
|
||||||
|
return this.parent.items.find(x => x.type === 'weapon' && x.system.equipped && x.system.secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
get getWeaponBurden() {
|
||||||
|
return this.primaryWeapon?.system?.burden === burden.twoHanded.value ||
|
||||||
|
(this.primaryWeapon && this.secondaryWeapon)
|
||||||
|
? burden.twoHanded.value
|
||||||
|
: this.primaryWeapon || this.secondaryWeapon
|
||||||
|
? burden.oneHanded.value
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get refreshableFeatures() {
|
||||||
|
return this.parent.items.reduce(
|
||||||
|
(acc, x) => {
|
||||||
|
if (x.type === 'feature' && x.system.refreshData?.type === 'feature' && x.system.refreshData?.type) {
|
||||||
|
acc[x.system.refreshData.type].push(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ shortRest: [], longRest: [] }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async unequipBeforeEquip(itemToEquip) {
|
||||||
|
const primary = this.primaryWeapon,
|
||||||
|
secondary = this.secondaryWeapon;
|
||||||
|
if (itemToEquip.system.secondary) {
|
||||||
|
if (primary && primary.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
||||||
|
await primary.update({ 'system.equipped': false });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondary) {
|
||||||
|
await secondary.update({ 'system.equipped': false });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (secondary && itemToEquip.system.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
||||||
|
await secondary.update({ 'system.equipped': false });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primary) {
|
||||||
|
await primary.update({ 'system.equipped': false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareBaseData() {
|
||||||
|
for (var attributeKey in this.traits) {
|
||||||
|
const attribute = this.traits[attributeKey];
|
||||||
|
/* Levleup handling */
|
||||||
|
}
|
||||||
|
|
||||||
|
const armor = this.armor;
|
||||||
|
this.damageThresholds = {
|
||||||
|
major: armor
|
||||||
|
? armor.system.baseThresholds.major + this.levelData.level.current
|
||||||
|
: this.levelData.level.current,
|
||||||
|
severe: armor
|
||||||
|
? armor.system.baseThresholds.severe + this.levelData.level.current
|
||||||
|
: this.levelData.level.current * 2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareDerivedData() {
|
||||||
|
this.resources.hope.max -= Object.keys(this.scars).length;
|
||||||
|
this.resources.hope.value = Math.min(this.resources.hope.value, this.resources.hope.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DhPCLevelData extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
return {
|
||||||
|
level: new fields.SchemaField({
|
||||||
|
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||||
|
changed: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||||
|
}),
|
||||||
|
levelups: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
achievements: new fields.SchemaField(
|
||||||
|
{
|
||||||
|
experiences: new fields.TypedObjectField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
name: new fields.StringField({ required: true }),
|
||||||
|
modifier: new fields.NumberField({ required: true, integer: true })
|
||||||
|
})
|
||||||
|
),
|
||||||
|
domainCards: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
uuid: new fields.StringField({ required: true }),
|
||||||
|
itemUuid: new fields.StringField({ required: true })
|
||||||
|
})
|
||||||
|
),
|
||||||
|
proficiency: new fields.NumberField({ integer: true })
|
||||||
|
},
|
||||||
|
{ nullable: true, initial: null }
|
||||||
|
),
|
||||||
|
selections: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
tier: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
level: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
optionKey: new fields.StringField({ required: true }),
|
||||||
|
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
||||||
|
checkboxNr: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
value: new fields.NumberField({ integer: true }),
|
||||||
|
minCost: new fields.NumberField({ integer: true }),
|
||||||
|
amount: new fields.NumberField({ integer: true }),
|
||||||
|
data: new fields.ArrayField(new fields.StringField({ required: true })),
|
||||||
|
secondaryData: new fields.StringField(),
|
||||||
|
itemUuid: new fields.StringField({ required: true })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get canLevelUp() {
|
||||||
|
return this.level.current < this.level.changed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ export default class DHClass extends BaseDataItem {
|
||||||
const allowed = await super._preCreate(data, options, user);
|
const allowed = await super._preCreate(data, options, user);
|
||||||
if (allowed === false) return;
|
if (allowed === false) return;
|
||||||
|
|
||||||
if (this.actor?.type === 'pc') {
|
if (this.actor?.type === 'character') {
|
||||||
const path = data.system.isMulticlass ? 'system.multiclass.value' : 'system.class.value';
|
const path = data.system.isMulticlass ? 'system.multiclass.value' : 'system.class.value';
|
||||||
if (foundry.utils.getProperty(this.actor, path)) {
|
if (foundry.utils.getProperty(this.actor, path)) {
|
||||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.ClassAlreadySelected'));
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.ClassAlreadySelected'));
|
||||||
|
|
@ -67,7 +67,7 @@ export default class DHClass extends BaseDataItem {
|
||||||
|
|
||||||
_onCreate(data, options, userId) {
|
_onCreate(data, options, userId) {
|
||||||
super._onCreate(data, options, userId);
|
super._onCreate(data, options, userId);
|
||||||
if (options.parent?.type === 'pc') {
|
if (options.parent?.type === 'character') {
|
||||||
const path = `system.${data.system.isMulticlass ? 'multiclass.value' : 'class.value'}`;
|
const path = `system.${data.system.isMulticlass ? 'multiclass.value' : 'class.value'}`;
|
||||||
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
|
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +76,7 @@ export default class DHClass extends BaseDataItem {
|
||||||
_onDelete(options, userId) {
|
_onDelete(options, userId) {
|
||||||
super._onDelete(options, userId);
|
super._onDelete(options, userId);
|
||||||
|
|
||||||
if (options.parent?.type === 'pc') {
|
if (options.parent?.type === 'character') {
|
||||||
const path = `system.${this.isMulticlass ? 'multiclass' : 'class'}`;
|
const path = `system.${this.isMulticlass ? 'multiclass' : 'class'}`;
|
||||||
options.parent.update({
|
options.parent.update({
|
||||||
[`${path}.value`]: null
|
[`${path}.value`]: null
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import DaggerheartAction from "../action.mjs";
|
import DaggerheartAction from '../action.mjs';
|
||||||
import BaseDataItem from "./base.mjs";
|
import BaseDataItem from './base.mjs';
|
||||||
|
|
||||||
export default class DHDomainCard extends BaseDataItem {
|
export default class DHDomainCard 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.domainCard",
|
label: 'TYPES.Item.domainCard',
|
||||||
type: "domainCard",
|
type: 'domainCard',
|
||||||
hasDescription: true,
|
hasDescription: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,10 +19,37 @@ export default class DHDomainCard extends BaseDataItem {
|
||||||
domain: new fields.StringField({ choices: SYSTEM.DOMAIN.domains, required: true, blank: true }),
|
domain: new fields.StringField({ choices: SYSTEM.DOMAIN.domains, required: true, blank: true }),
|
||||||
level: new fields.NumberField({ initial: 1, integer: true }),
|
level: new fields.NumberField({ initial: 1, integer: true }),
|
||||||
recallCost: new fields.NumberField({ initial: 0, integer: true }),
|
recallCost: new fields.NumberField({ initial: 0, integer: true }),
|
||||||
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(DaggerheartAction))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _preCreate(data, options, user) {
|
||||||
|
const allowed = await super._preCreate(data, options, user);
|
||||||
|
if (allowed === false) return;
|
||||||
|
|
||||||
|
if (this.actor?.type === 'character') {
|
||||||
|
if (!this.actor.system.class.value) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.NoClassSelected'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.actor.system.domains.find(x => x === item.system.domain)) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.LacksDomain'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.actor.system.domainCards.total.length === 5) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.MaxLoadoutReached'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.actor.system.domainCards.total.find(x => x.name === item.name)) {
|
||||||
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Item.Errors.DuplicateDomainCard'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ export default class DHSubclass extends BaseDataItem {
|
||||||
const allowed = await super._preCreate(data, options, user);
|
const allowed = await super._preCreate(data, options, user);
|
||||||
if (allowed === false) return;
|
if (allowed === false) return;
|
||||||
|
|
||||||
if (this.actor?.type === 'pc') {
|
if (this.actor?.type === 'character') {
|
||||||
const path = data.system.isMulticlass ? 'system.multiclass' : 'system.class';
|
const path = data.system.isMulticlass ? 'system.multiclass' : 'system.class';
|
||||||
const classData = foundry.utils.getProperty(this.actor, path);
|
const classData = foundry.utils.getProperty(this.actor, path);
|
||||||
if (!classData.value) {
|
if (!classData.value) {
|
||||||
|
|
@ -52,7 +52,7 @@ export default class DHSubclass extends BaseDataItem {
|
||||||
_onCreate(data, options, userId) {
|
_onCreate(data, options, userId) {
|
||||||
super._onCreate(data, options, userId);
|
super._onCreate(data, options, userId);
|
||||||
|
|
||||||
if (options.parent?.type === 'pc') {
|
if (options.parent?.type === 'character') {
|
||||||
const path = `system.${data.system.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
|
const path = `system.${data.system.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
|
||||||
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
|
options.parent.update({ [path]: `${options.parent.uuid}.Item.${data._id}` });
|
||||||
}
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ export default class DHSubclass extends BaseDataItem {
|
||||||
_onDelete(options, userId) {
|
_onDelete(options, userId) {
|
||||||
super._onDelete(options, userId);
|
super._onDelete(options, userId);
|
||||||
|
|
||||||
if (options.parent?.type === 'pc') {
|
if (options.parent?.type === 'character') {
|
||||||
const path = `system.${this.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
|
const path = `system.${this.isMulticlass ? 'multiclass.subclass' : 'class.subclass'}`;
|
||||||
options.parent.update({ [path]: null });
|
options.parent.update({ [path]: null });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,413 +0,0 @@
|
||||||
import { getPathValue } from '../helpers/utils.mjs';
|
|
||||||
import ForeignDocumentUUIDField from './fields/foreignDocumentUUIDField.mjs';
|
|
||||||
import { LevelOptionType } from './levelTier.mjs';
|
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
|
||||||
|
|
||||||
const attributeField = () =>
|
|
||||||
new fields.SchemaField({
|
|
||||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
base: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
tierMarked: new fields.BooleanField({ required: true, initial: false })
|
|
||||||
});
|
|
||||||
|
|
||||||
const resourceField = max =>
|
|
||||||
new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
bonus: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
min: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
baseMax: new fields.NumberField({ initial: max, integer: true })
|
|
||||||
});
|
|
||||||
|
|
||||||
export default class DhpPC extends foundry.abstract.TypeDataModel {
|
|
||||||
static defineSchema() {
|
|
||||||
return {
|
|
||||||
resources: new fields.SchemaField({
|
|
||||||
hitPoints: resourceField(6),
|
|
||||||
stress: resourceField(6),
|
|
||||||
hope: new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ initial: -1, integer: true }), // FIXME. Logic is gte and needs -1 in PC/Hope. Change to 0
|
|
||||||
min: new fields.NumberField({ initial: 0, integer: true })
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
bonuses: new fields.SchemaField({
|
|
||||||
damage: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
value: new fields.NumberField({ integer: true, initial: 0 }),
|
|
||||||
type: new fields.StringField({ nullable: true }),
|
|
||||||
initiallySelected: new fields.BooleanField(),
|
|
||||||
hopeIncrease: new fields.StringField({ initial: null, nullable: true }),
|
|
||||||
description: new fields.StringField({})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
traits: new fields.SchemaField({
|
|
||||||
agility: attributeField(),
|
|
||||||
strength: attributeField(),
|
|
||||||
finesse: attributeField(),
|
|
||||||
instinct: attributeField(),
|
|
||||||
presence: attributeField(),
|
|
||||||
knowledge: attributeField()
|
|
||||||
}),
|
|
||||||
proficiency: new fields.SchemaField({
|
|
||||||
base: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
|
||||||
bonus: new fields.NumberField({ required: true, initial: 0, integer: true })
|
|
||||||
}),
|
|
||||||
evasion: new fields.SchemaField({
|
|
||||||
bonus: new fields.NumberField({ initial: 0, integer: true })
|
|
||||||
}),
|
|
||||||
experiences: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
id: new fields.StringField({ required: true }),
|
|
||||||
description: new fields.StringField({}),
|
|
||||||
value: new fields.NumberField({ integer: true, nullable: true, initial: null })
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
initial: [
|
|
||||||
{ id: foundry.utils.randomID(), description: '', value: 2 },
|
|
||||||
{ id: foundry.utils.randomID(), description: '', value: 2 }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
gold: new fields.SchemaField({
|
|
||||||
coins: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
handfulls: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
bags: new fields.NumberField({ initial: 0, integer: true }),
|
|
||||||
chests: new fields.NumberField({ initial: 0, integer: true })
|
|
||||||
}),
|
|
||||||
pronouns: new fields.StringField({}),
|
|
||||||
domainData: new fields.SchemaField({
|
|
||||||
maxLoadout: new fields.NumberField({ initial: 2, integer: true }),
|
|
||||||
maxCards: new fields.NumberField({ initial: 2, integer: true })
|
|
||||||
}),
|
|
||||||
story: new fields.SchemaField({
|
|
||||||
background: new fields.HTMLField(),
|
|
||||||
appearance: new fields.HTMLField(),
|
|
||||||
connections: new fields.HTMLField(),
|
|
||||||
scars: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
name: new fields.StringField({}),
|
|
||||||
description: new fields.HTMLField()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
description: new fields.StringField({}),
|
|
||||||
//Temporary until new FoundryVersion fix --> See Armor.Mjs DataPreparation
|
|
||||||
armorMarks: new fields.SchemaField({
|
|
||||||
max: new fields.NumberField({ initial: 6, integer: true }),
|
|
||||||
value: new fields.NumberField({ initial: 0, integer: true })
|
|
||||||
}),
|
|
||||||
class: new fields.SchemaField({
|
|
||||||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
|
||||||
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
|
||||||
}),
|
|
||||||
multiclass: new fields.SchemaField({
|
|
||||||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
|
||||||
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
|
||||||
}),
|
|
||||||
levelData: new fields.EmbeddedDataField(DhPCLevelData)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get tier() {
|
|
||||||
return this.#getTier(this.levelData.currentLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
get ancestry() {
|
|
||||||
return this.parent.items.find(x => x.type === 'ancestry') ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get multiclassSubclass() {
|
|
||||||
return this.parent.items.find(x => x.type === 'subclass' && x.system.multiclass) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get subclass() {
|
|
||||||
return this.parent.items.find(x => x.type === 'subclass' && !x.system.multiclass) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get subclassFeatures() {
|
|
||||||
const subclass = this.subclass;
|
|
||||||
const multiclass = this.multiclassSubclass;
|
|
||||||
const subclassItems = this.parent.items.filter(x => x.type === 'feature' && x.system.type === 'subclass');
|
|
||||||
return {
|
|
||||||
subclass: !subclass
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
foundation: subclassItems.filter(x =>
|
|
||||||
subclass.system.foundationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
|
||||||
),
|
|
||||||
specialization: subclassItems.filter(x =>
|
|
||||||
subclass.system.specializationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
|
||||||
),
|
|
||||||
mastery: subclassItems.filter(x =>
|
|
||||||
subclass.system.masteryFeature.abilities.some(ability => ability.uuid === x.uuid)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
multiclassSubclass: !multiclass
|
|
||||||
? {}
|
|
||||||
: {
|
|
||||||
foundation: subclassItems.filter(x =>
|
|
||||||
multiclass.system.foundationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
|
||||||
),
|
|
||||||
specialization: subclassItems.filter(x =>
|
|
||||||
multiclass.system.specializationFeature.abilities.some(ability => ability.uuid === x.uuid)
|
|
||||||
),
|
|
||||||
mastery: subclassItems.filter(x =>
|
|
||||||
multiclass.system.masteryFeature.abilities.some(ability => ability.uuid === x.uuid)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get community() {
|
|
||||||
return this.parent.items.find(x => x.type === 'community') ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get classFeatures() {
|
|
||||||
return this.parent.items.filter(
|
|
||||||
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.class.id && !x.system.multiclass
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get multiclassFeatures() {
|
|
||||||
return this.parent.items.filter(
|
|
||||||
x => x.type === 'feature' && x.system.type === SYSTEM.ITEM.featureTypes.class.id && x.system.multiclass
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get domains() {
|
|
||||||
const classDomains = this.class ? this.class.system.domains : [];
|
|
||||||
const multiclassDomains = this.multiclass ? this.multiclass.system.domains : [];
|
|
||||||
return [...classDomains, ...multiclassDomains];
|
|
||||||
}
|
|
||||||
|
|
||||||
get domainCards() {
|
|
||||||
const domainCards = this.parent.items.filter(x => x.type === 'domainCard');
|
|
||||||
const loadout = domainCards.filter(x => !x.system.inVault);
|
|
||||||
const vault = domainCards.filter(x => x.system.inVault);
|
|
||||||
|
|
||||||
return {
|
|
||||||
loadout: loadout,
|
|
||||||
vault: vault,
|
|
||||||
total: [...loadout, ...vault]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get armor() {
|
|
||||||
return this.parent.items.find(x => x.type === 'armor' && x.system.equipped);
|
|
||||||
}
|
|
||||||
|
|
||||||
get equippedWeapons() {
|
|
||||||
const primaryWeapon = this.parent.items.find(
|
|
||||||
x => x.type === 'weapon' && x.system.equipped && !x.system.secondary
|
|
||||||
);
|
|
||||||
const secondaryWeapon = this.parent.items.find(
|
|
||||||
x => x.type === 'weapon' && x.system.equipped && x.system.secondary
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
primary: this.#weaponData(primaryWeapon),
|
|
||||||
secondary: this.#weaponData(secondaryWeapon),
|
|
||||||
burden: this.getBurden(primaryWeapon, secondaryWeapon)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static async unequipBeforeEquip(itemToEquip) {
|
|
||||||
const equippedWeapons = this.equippedWeapons;
|
|
||||||
|
|
||||||
if (itemToEquip.system.secondary) {
|
|
||||||
if (equippedWeapons.primary && equippedWeapons.primary.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
|
||||||
await this.parent.items.get(equippedWeapons.primary.id).update({ 'system.equipped': false });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equippedWeapons.secondary) {
|
|
||||||
await this.parent.items.get(equippedWeapons.secondary.id).update({ 'system.equipped': false });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (equippedWeapons.secondary && itemToEquip.system.burden === SYSTEM.GENERAL.burden.twoHanded.value) {
|
|
||||||
await this.parent.items.get(equippedWeapons.secondary.id).update({ 'system.equipped': false });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (equippedWeapons.primary) {
|
|
||||||
await this.parent.items.get(equippedWeapons.primary.id).update({ 'system.equipped': false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get effects() {
|
|
||||||
return this.parent.items.reduce((acc, item) => {
|
|
||||||
const effects = item.system.effectData;
|
|
||||||
if (effects && !item.system.disabled) {
|
|
||||||
for (var key in effects) {
|
|
||||||
const effect = effects[key];
|
|
||||||
for (var effectEntry of effect) {
|
|
||||||
if (!acc[key]) acc[key] = [];
|
|
||||||
acc[key].push({ name: item.name, value: effectEntry });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
get refreshableFeatures() {
|
|
||||||
return this.parent.items.reduce(
|
|
||||||
(acc, x) => {
|
|
||||||
if (x.type === 'feature' && x.system.refreshData?.type === 'feature' && x.system.refreshData?.type) {
|
|
||||||
acc[x.system.refreshData.type].push(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
|
||||||
},
|
|
||||||
{ shortRest: [], longRest: [] }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Should not be done in data?
|
|
||||||
//TODO: REMOVE THIS
|
|
||||||
#weaponData(weapon) {
|
|
||||||
return weapon
|
|
||||||
? {
|
|
||||||
id: weapon.id,
|
|
||||||
name: weapon.name,
|
|
||||||
trait: game.i18n.localize(CONFIG.daggerheart.ACTOR.abilities[weapon.system.trait].label),
|
|
||||||
range: CONFIG.daggerheart.GENERAL.range[weapon.system.range],
|
|
||||||
damage: {
|
|
||||||
value: weapon.system.damage.value,
|
|
||||||
type: CONFIG.daggerheart.GENERAL.damageTypes[weapon.system.damage.type]
|
|
||||||
},
|
|
||||||
burden: weapon.system.burden,
|
|
||||||
feature: CONFIG.daggerheart.ITEM.weaponFeatures[weapon.system.feature],
|
|
||||||
img: weapon.img,
|
|
||||||
uuid: weapon.uuid
|
|
||||||
}
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareBaseData() {
|
|
||||||
this.resources.hitPoints.max = this.resources.hitPoints.baseMax + this.resources.hitPoints.bonus;
|
|
||||||
this.resources.stress.max = this.resources.stress.baseMax + this.resources.stress.bonus;
|
|
||||||
this.evasion.value = (this.class?.system?.evasion ?? 0) + this.evasion.bonus;
|
|
||||||
this.proficiency.value = this.proficiency.base + this.proficiency.bonus;
|
|
||||||
|
|
||||||
for (var attributeKey in this.traits) {
|
|
||||||
const attribute = this.traits[attributeKey];
|
|
||||||
attribute.value = attribute.base + attribute.bonus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
prepareDerivedData() {
|
|
||||||
this.resources.hope.max = 6 - this.story.scars.length;
|
|
||||||
if (this.resources.hope.value >= this.resources.hope.max) {
|
|
||||||
this.resources.hope.value = Math.max(this.resources.hope.max - 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const armor = this.armor;
|
|
||||||
this.damageThresholds = {
|
|
||||||
major: armor
|
|
||||||
? armor.system.baseThresholds.major + this.levelData.level.current
|
|
||||||
: this.levelData.level.current,
|
|
||||||
severe: armor
|
|
||||||
? armor.system.baseThresholds.severe + this.levelData.level.current
|
|
||||||
: this.levelData.level.current * 2
|
|
||||||
};
|
|
||||||
|
|
||||||
this.applyEffects();
|
|
||||||
}
|
|
||||||
|
|
||||||
applyEffects() {
|
|
||||||
const effects = this.effects;
|
|
||||||
for (var key in effects) {
|
|
||||||
const effectType = effects[key];
|
|
||||||
for (var effect of effectType) {
|
|
||||||
switch (key) {
|
|
||||||
case SYSTEM.EFFECTS.effectTypes.health.id:
|
|
||||||
this.resources.hitPoints.bonus += effect.value.valueData.value;
|
|
||||||
break;
|
|
||||||
case SYSTEM.EFFECTS.effectTypes.stress.id:
|
|
||||||
this.resources.stress.bonus += effect.value.valueData.value;
|
|
||||||
break;
|
|
||||||
case SYSTEM.EFFECTS.effectTypes.damage.id:
|
|
||||||
this.bonuses.damage.push({
|
|
||||||
value: getPathValue(effect.value.valueData.value, this),
|
|
||||||
type: 'physical',
|
|
||||||
description: effect.name,
|
|
||||||
hopeIncrease: effect.value.valueData.hopeIncrease,
|
|
||||||
initiallySelected: effect.value.initiallySelected,
|
|
||||||
appliesOn: effect.value.appliesOn
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getBurden(primary, secondary) {
|
|
||||||
const twoHanded =
|
|
||||||
primary?.system?.burden === 'twoHanded' ||
|
|
||||||
secondary?.system?.burden === 'twoHanded' ||
|
|
||||||
(primary?.system?.burden === 'oneHanded' && secondary?.system?.burden === 'oneHanded');
|
|
||||||
const oneHanded =
|
|
||||||
!twoHanded && (primary?.system?.burden === 'oneHanded' || secondary?.system?.burden === 'oneHanded');
|
|
||||||
|
|
||||||
return twoHanded ? 'twoHanded' : oneHanded ? 'oneHanded' : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#getTier(level) {
|
|
||||||
if (level >= 8) return 3;
|
|
||||||
else if (level >= 5) return 2;
|
|
||||||
else if (level >= 2) return 1;
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DhPCLevelData extends foundry.abstract.DataModel {
|
|
||||||
static defineSchema() {
|
|
||||||
return {
|
|
||||||
level: new fields.SchemaField({
|
|
||||||
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
|
||||||
changed: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
|
||||||
}),
|
|
||||||
levelups: new fields.TypedObjectField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
achievements: new fields.SchemaField(
|
|
||||||
{
|
|
||||||
experiences: new fields.TypedObjectField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
name: new fields.StringField({ required: true }),
|
|
||||||
modifier: new fields.NumberField({ required: true, integer: true })
|
|
||||||
})
|
|
||||||
),
|
|
||||||
domainCards: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
uuid: new fields.StringField({ required: true }),
|
|
||||||
itemUuid: new fields.StringField({ required: true })
|
|
||||||
})
|
|
||||||
),
|
|
||||||
proficiency: new fields.NumberField({ integer: true })
|
|
||||||
},
|
|
||||||
{ nullable: true, initial: null }
|
|
||||||
),
|
|
||||||
selections: new fields.ArrayField(
|
|
||||||
new fields.SchemaField({
|
|
||||||
tier: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
level: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
optionKey: new fields.StringField({ required: true }),
|
|
||||||
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
|
||||||
checkboxNr: new fields.NumberField({ required: true, integer: true }),
|
|
||||||
value: new fields.NumberField({ integer: true }),
|
|
||||||
minCost: new fields.NumberField({ integer: true }),
|
|
||||||
amount: new fields.NumberField({ integer: true }),
|
|
||||||
data: new fields.ArrayField(new fields.StringField({ required: true })),
|
|
||||||
secondaryData: new fields.StringField(),
|
|
||||||
itemUuid: new fields.StringField({ required: true })
|
|
||||||
})
|
|
||||||
)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get canLevelUp() {
|
|
||||||
return this.level.current < this.level.changed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
export default class DhVariantRules extends foundry.abstract.DataModel {
|
export default class DhVariantRules extends foundry.abstract.DataModel {
|
||||||
|
static LOCALIZATION_PREFIXES = ['DAGGERHEART.Settings.VariantRules'];
|
||||||
|
|
||||||
static defineSchema() {
|
static defineSchema() {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
return {
|
return {
|
||||||
actionTokens: new fields.SchemaField({
|
actionTokens: new fields.SchemaField({
|
||||||
enabled: new fields.BooleanField({ required: true, initial: false }),
|
enabled: new fields.BooleanField({ required: true, initial: false }),
|
||||||
tokens: new fields.NumberField({ required: true, integer: true, initial: 3 })
|
tokens: new fields.NumberField({ required: true, integer: true, initial: 3 })
|
||||||
})
|
}),
|
||||||
|
useCoins: new fields.BooleanField({ initial: false })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ export default class DhpActor extends Actor {
|
||||||
|
|
||||||
// Configure prototype token settings
|
// Configure prototype token settings
|
||||||
const prototypeToken = {};
|
const prototypeToken = {};
|
||||||
if (this.type === 'pc')
|
if (this.type === 'character')
|
||||||
Object.assign(prototypeToken, {
|
Object.assign(prototypeToken, {
|
||||||
sight: { enabled: true },
|
sight: { enabled: true },
|
||||||
actorLink: true,
|
actorLink: true,
|
||||||
|
|
@ -28,7 +28,7 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateLevel(newLevel) {
|
async updateLevel(newLevel) {
|
||||||
if (this.type !== 'pc' || newLevel === this.system.levelData.level.changed) return;
|
if (this.type !== 'character' || newLevel === this.system.levelData.level.changed) return;
|
||||||
|
|
||||||
if (newLevel > this.system.levelData.level.current) {
|
if (newLevel > this.system.levelData.level.current) {
|
||||||
await this.update({ 'system.levelData.level.changed': newLevel });
|
await this.update({ 'system.levelData.level.changed': newLevel });
|
||||||
|
|
@ -124,7 +124,7 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
async diceRoll(modifier, shiftKey) {
|
async diceRoll(modifier, shiftKey) {
|
||||||
if (this.type === 'pc') {
|
if (this.type === 'character') {
|
||||||
return await this.dualityRoll(modifier, shiftKey);
|
return await this.dualityRoll(modifier, shiftKey);
|
||||||
} else {
|
} else {
|
||||||
return await this.npcRoll(modifier, shiftKey);
|
return await this.npcRoll(modifier, shiftKey);
|
||||||
|
|
@ -173,12 +173,11 @@ export default class DhpActor extends Actor {
|
||||||
return { roll, dice: dice[0], modifiers, advantageState: advantage === true ? 1 : advantage === false ? 2 : 0 };
|
return { roll, dice: dice[0], modifiers, advantageState: advantage === true ? 1 : advantage === false ? 2 : 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
async dualityRoll(modifier, shiftKey, bonusDamage = []) {
|
async dualityRoll(modifier, shiftKey) {
|
||||||
let hopeDice = 'd12',
|
let hopeDice = 'd12',
|
||||||
fearDice = 'd12',
|
fearDice = 'd12',
|
||||||
advantageDice = null,
|
advantageDice = null,
|
||||||
disadvantageDice = null,
|
disadvantageDice = null;
|
||||||
bonusDamageString = '';
|
|
||||||
|
|
||||||
const modifiers =
|
const modifiers =
|
||||||
modifier.value !== null
|
modifier.value !== null
|
||||||
|
|
@ -195,12 +194,9 @@ export default class DhpActor extends Actor {
|
||||||
: [];
|
: [];
|
||||||
if (!shiftKey) {
|
if (!shiftKey) {
|
||||||
const dialogClosed = new Promise((resolve, _) => {
|
const dialogClosed = new Promise((resolve, _) => {
|
||||||
new RollSelectionDialog(
|
new RollSelectionDialog(this.system.experiences, this.system.resources.hope.value, resolve).render(
|
||||||
this.system.experiences,
|
true
|
||||||
bonusDamage,
|
);
|
||||||
this.system.resources.hope.value,
|
|
||||||
resolve
|
|
||||||
).render(true);
|
|
||||||
});
|
});
|
||||||
const result = await dialogClosed;
|
const result = await dialogClosed;
|
||||||
(hopeDice = result.hope),
|
(hopeDice = result.hope),
|
||||||
|
|
@ -214,7 +210,6 @@ export default class DhpActor extends Actor {
|
||||||
title: x.description
|
title: x.description
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
bonusDamageString = result.bonusDamage;
|
|
||||||
|
|
||||||
const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
const automateHope = await game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Automation.Hope);
|
||||||
|
|
||||||
|
|
@ -268,8 +263,7 @@ export default class DhpActor extends Actor {
|
||||||
fear: { dice: fearDice, value: fear },
|
fear: { dice: fearDice, value: fear },
|
||||||
advantage: { dice: advantageDice, value: advantage },
|
advantage: { dice: advantageDice, value: advantage },
|
||||||
disadvantage: { dice: disadvantageDice, value: disadvantage },
|
disadvantage: { dice: disadvantageDice, value: disadvantage },
|
||||||
modifiers: modifiers,
|
modifiers: modifiers
|
||||||
bonusDamageString
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,11 +395,6 @@ export default class DhpActor extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async emulateItemDrop(data) {
|
|
||||||
const event = new DragEvent('drop', { altKey: game.keyboard.isModifierActive('Alt') });
|
|
||||||
return this.sheet._onDropItem(event, { data: data });
|
|
||||||
}
|
|
||||||
|
|
||||||
//Move to action-scope?
|
//Move to action-scope?
|
||||||
async useAction(action) {
|
async useAction(action) {
|
||||||
const userTargets = Array.from(game.user.targets);
|
const userTargets = Array.from(game.user.targets);
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ export const getCommandTarget = () => {
|
||||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.NoSelectedToken'));
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.NoSelectedToken'));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (target.type !== 'pc') {
|
if (target.type !== 'character') {
|
||||||
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.OnlyUseableByPC'));
|
ui.notifications.error(game.i18n.localize('DAGGERHEART.Notification.Error.OnlyUseableByPC'));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
@import './resources.less';
|
@import './resources.less';
|
||||||
|
|
||||||
// new styles imports
|
// new styles imports
|
||||||
|
@import './less/actors/character.less';
|
||||||
@import './less/actors/adversary.less';
|
@import './less/actors/adversary.less';
|
||||||
@import './less/actors/environment.less';
|
@import './less/actors/environment.less';
|
||||||
|
|
||||||
|
|
|
||||||
0
styles/less/actors/character.less
Normal file
0
styles/less/actors/character.less
Normal file
|
|
@ -203,7 +203,9 @@
|
||||||
},
|
},
|
||||||
"documentTypes": {
|
"documentTypes": {
|
||||||
"Actor": {
|
"Actor": {
|
||||||
"pc": {},
|
"character": {
|
||||||
|
"htmlFields": ["story", "description", "scars.*.description"]
|
||||||
|
},
|
||||||
"adversary": {
|
"adversary": {
|
||||||
"htmlFields": ["description", "motivesAndTactics"]
|
"htmlFields": ["description", "motivesAndTactics"]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{formGroup settingFields.schema.fields.useCoins value=settingFields._source.useCoins localize=true }}
|
||||||
|
|
||||||
<footer class="form-footer">
|
<footer class="form-footer">
|
||||||
<button data-action="reset">
|
<button data-action="reset">
|
||||||
<i class="fa-solid fa-arrow-rotate-left"></i>
|
<i class="fa-solid fa-arrow-rotate-left"></i>
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@
|
||||||
{{#if document.system.levelData.canLevelUp}}<div class="levelup-marker">*</div>{{/if}}
|
{{#if document.system.levelData.canLevelUp}}<div class="levelup-marker">*</div>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||||
<div class="level-title {{#if document.system.levelData.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
<div data-action="levelup" class="level-title {{#if document.system.levelData.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
<button data-action="selectCommunity" class="option-select deeper"><i class="fa-solid fa-house-chimney"></i></button>
|
<button data-action="selectCommunity" class="option-select deeper"><i class="fa-solid fa-house-chimney"></i></button>
|
||||||
<button data-action="selectAncestry" class="option-select"><i class="fa-solid fa-user-large"></i></button>
|
<button data-action="selectAncestry" class="option-select"><i class="fa-solid fa-user-large"></i></button>
|
||||||
{{/objectSelector}}
|
{{/objectSelector}}
|
||||||
{{#objectSelector title="Subclass" ids=(join document.system.subclass.uuid) values=(join document.system.subclass.name) titleFontSize=14}}
|
{{#objectSelector title="Subclass" ids=(join document.system.class.subclass.uuid) values=(join document.system.class.subclass.name) titleFontSize=14}}
|
||||||
<button data-action="selectSubclass" class="option-select" {{#if (not ../document.system.class.value)}}disabled{{/if}}><i class="fa-solid fa-fw fa-search"></i></button>
|
<button data-action="selectSubclass" class="option-select" {{#if (not ../document.system.class.value)}}disabled{{/if}}><i class="fa-solid fa-fw fa-search"></i></button>
|
||||||
{{/objectSelector}}
|
{{/objectSelector}}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -87,7 +87,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="body-section flex3">
|
<div class="body-section flex3">
|
||||||
{{> "systems/daggerheart/templates/sheets/parts/attributes.hbs" }}
|
{{> "systems/daggerheart/templates/sheets/parts/attributes.hbs" }}
|
||||||
{{> "systems/daggerheart/templates/sheets/parts/weapons.hbs" weapons=document.system.equippedWeapons proficiency=document.system.proficiency.value }}
|
{{> "systems/daggerheart/templates/sheets/parts/weapons.hbs" primaryWeapon=document.system.primaryWeapon secondaryWeapon=document.system.secondaryWeapon weaponBurden=document.system.getWeaponBurden proficiency=document.system.proficiency }}
|
||||||
{{> "systems/daggerheart/templates/sheets/parts/armor.hbs" armor=document.system.armor }}
|
{{> "systems/daggerheart/templates/sheets/parts/armor.hbs" armor=document.system.armor }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -95,10 +95,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab domain-card-tab {{this.tabs.primary.loadout.cssClass}}" data-group="primary" data-tab="loadout">
|
<div class="tab domain-card-tab {{this.tabs.primary.loadout.cssClass}}" data-group="primary" data-tab="loadout">
|
||||||
{{> "systems/daggerheart/templates/sheets/pc/sections/loadout.hbs" abilities=this.abilities actor=this.document config=this.config }}
|
{{> "systems/daggerheart/templates/sheets/character/sections/loadout.hbs" abilities=this.abilities actor=this.document config=this.config }}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab {{this.tabs.primary.inventory.cssClass}}" data-group="primary" data-tab="inventory">
|
<div class="tab {{this.tabs.primary.inventory.cssClass}}" data-group="primary" data-tab="inventory">
|
||||||
{{> "systems/daggerheart/templates/sheets/pc/sections/inventory.hbs" inventory=this.inventory }}
|
{{> "systems/daggerheart/templates/sheets/character/sections/inventory.hbs" inventory=this.inventory }}
|
||||||
</div>
|
</div>
|
||||||
<div class="tab {{this.tabs.primary.story.cssClass}}" data-group="primary" data-tab="story">
|
<div class="tab {{this.tabs.primary.story.cssClass}}" data-group="primary" data-tab="story">
|
||||||
<div class="flexcol" style="height: 100%;">
|
<div class="flexcol" style="height: 100%;">
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
<div class="card-row">
|
<div class="card-row">
|
||||||
<div class="heritage-card {{#if (not abilities.foundation.ancestry)}}outlined{{/if}}">
|
<div class="heritage-card {{#if (not abilities.foundation.ancestry)}}outlined{{/if}}">
|
||||||
{{#if abilities.foundation.ancestry}}
|
{{#if abilities.foundation.ancestry}}
|
||||||
{{> "systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs" card=abilities.foundation.ancestry }}
|
{{> "systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs" card=abilities.foundation.ancestry }}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="empty-ability-container">
|
<div class="empty-ability-container">
|
||||||
<div class="empty-ability-inner-container">
|
<div class="empty-ability-inner-container">
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="heritage-card {{#if (not abilities.foundation.community)}}outlined{{/if}}">
|
<div class="heritage-card {{#if (not abilities.foundation.community)}}outlined{{/if}}">
|
||||||
{{#if abilities.foundation.community}}
|
{{#if abilities.foundation.community}}
|
||||||
{{> "systems/daggerheart/templates/sheets/pc/parts/heritageCard.hbs" card=abilities.foundation.community }}
|
{{> "systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs" card=abilities.foundation.community }}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="empty-ability-container">
|
<div class="empty-ability-container">
|
||||||
<div class="empty-ability-inner-container">
|
<div class="empty-ability-inner-container">
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
<div class="card-row">
|
<div class="card-row">
|
||||||
<div class="heritage-card {{#if (not abilities.foundation.advancement.foundation)}}outlined{{/if}}">
|
<div class="heritage-card {{#if (not abilities.foundation.advancement.foundation)}}outlined{{/if}}">
|
||||||
{{#if abilities.foundation.advancement.foundation}}
|
{{#if abilities.foundation.advancement.foundation}}
|
||||||
{{> "systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs" card=abilities.foundation.advancement.foundation }}
|
{{> "systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs" card=abilities.foundation.advancement.foundation }}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="empty-ability-container">
|
<div class="empty-ability-container">
|
||||||
<div class="empty-ability-inner-container">
|
<div class="empty-ability-inner-container">
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="heritage-card {{#if (not abilities.foundation.advancement.first)}}outlined{{/if}}">
|
<div class="heritage-card {{#if (not abilities.foundation.advancement.first)}}outlined{{/if}}">
|
||||||
{{#if abilities.foundation.advancement.first}}
|
{{#if abilities.foundation.advancement.first}}
|
||||||
{{> "systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs" card=abilities.foundation.advancement.first }}
|
{{> "systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs" card=abilities.foundation.advancement.first }}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="empty-ability-container">
|
<div class="empty-ability-container">
|
||||||
<div class="empty-ability-inner-container">
|
<div class="empty-ability-inner-container">
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="heritage-card {{#if (not abilities.foundation.advancement.second)}}outlined{{/if}}">
|
<div class="heritage-card {{#if (not abilities.foundation.advancement.second)}}outlined{{/if}}">
|
||||||
{{#if abilities.foundation.advancement.second}}
|
{{#if abilities.foundation.advancement.second}}
|
||||||
{{> "systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs" card=abilities.foundation.advancement.second}}
|
{{> "systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs" card=abilities.foundation.advancement.second}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="empty-ability-container">
|
<div class="empty-ability-container">
|
||||||
<div class="empty-ability-inner-container">
|
<div class="empty-ability-inner-container">
|
||||||
|
|
@ -12,10 +12,10 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="attribute-image">
|
<div class="attribute-image">
|
||||||
{{#if ../editAttributes}}
|
{{#if ../editAttributes}}
|
||||||
<select class="attribute-value{{#if (lt attribute.base 0)}} negative{{/if}}{{#if (and (not attribute.base) (not ../abilityScoresFinished))}} unselected{{/if}}" data-attribute="{{key}}">
|
<select class="attribute-value{{#if (lt attribute.value 0)}} negative{{/if}}{{#if (and (not attribute.value) (not ../abilityScoresFinished))}} unselected{{/if}}" data-attribute="{{key}}">
|
||||||
{{#if (not (eq attribute.base 0))}}<option value="">{{attribute.base}}</option>{{/if}}
|
{{#if (not (eq attribute.value 0))}}<option value="">{{attribute.value}}</option>{{/if}}
|
||||||
{{#each ../abilityScoreArray as |option|}}
|
{{#each ../abilityScoreArray as |option|}}
|
||||||
<option value="{{option.value}}"{{#if (eq option.value attribute.base)}} selected="selected"{{/if}}>{{option.name}}</option>
|
<option value="{{option.value}}">{{option.name}}</option>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</select>
|
</select>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="defense-row">
|
<div class="defense-row">
|
||||||
<div class="defense-section">
|
<div class="defense-section">
|
||||||
<div class="defense-container">
|
<div class="defense-container">
|
||||||
<div class="defense-value">{{document.system.evasion.value}}</div>
|
<div class="defense-value">{{document.system.evasion}}</div>
|
||||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||||
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Evasion"}}</div>
|
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Evasion"}}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -16,16 +16,18 @@
|
||||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||||
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Armor"}}</div>
|
<div class="defense-banner">{{localize "DAGGERHEART.Sheets.PC.Defense.Armor"}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="armor-marks">
|
{{#if document.system.armor.system.marks}}
|
||||||
{{#times (subtract 12 document.system.armorMarks.max)}}
|
<div class="armor-marks">
|
||||||
<input class="mark disabled-mark" type="checkbox" disabled />
|
{{#times (subtract 12 document.system.armor.system.marks.max)}}
|
||||||
{{/times}}
|
<input class="mark disabled-mark" type="checkbox" disabled />
|
||||||
{{#times document.system.armorMarks.max}}
|
{{/times}}
|
||||||
{{#with (add this 1)}}
|
{{#times document.system.armor.system.marks.max}}
|
||||||
<input class="mark" type="checkbox" data-action="toggleMarks" data-value="{{this}}" {{ checked (gte ../../document.system.armor.system.marks.value this) }} />
|
{{#with (add this 1)}}
|
||||||
{{/with}}
|
<input class="mark" type="checkbox" data-action="toggleMarks" data-value="{{this}}" {{ checked (gte ../../document.system.armor.system.marks.value this) }} />
|
||||||
{{/times}}
|
{{/with}}
|
||||||
</div>
|
{{/times}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
<fieldset class="left-main-container experience-container">
|
<fieldset class="left-main-container experience-container">
|
||||||
<legend class="legend">{{localize "DAGGERHEART.Sheets.PC.Experience.Title"}}</legend>
|
<legend class="legend">{{localize "DAGGERHEART.Sheets.PC.Experience.Title"}}</legend>
|
||||||
{{#each document.system.experiences as |experience index|}}
|
{{#each document.system.experiences as |experience id|}}
|
||||||
<div class="experience-row">
|
<div class="experience-row">
|
||||||
<input class="experience-description" data-index={{index}} value="{{experience.description}}" type="text" />
|
<input name="{{concat "system.experiences." id ".description"}}" data-experience={{id}} value="{{experience.description}}" type="text" />
|
||||||
<div class="experience-value">{{experience.value}}</div>
|
<div name="{{concat "system.experiences." id ".value"}}" class="experience-value">{{experience.value}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{#times (subtract 5 document.system.experiences.length)}}
|
{{#times (subtract 5 (length document.system.experiences))}}
|
||||||
<div class="experience-row">
|
<div class="experience-row">
|
||||||
<input type="text" class="disabled-experience" disabled />
|
<input type="text" class="disabled-experience" disabled />
|
||||||
<div class="experience-value empty"></div>
|
<div class="experience-value empty"></div>
|
||||||
|
|
|
||||||
|
|
@ -11,39 +11,39 @@
|
||||||
<div class="proficiency-container-visual-element"></div>
|
<div class="proficiency-container-visual-element"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="weapons-burden">
|
<div class="weapons-burden">
|
||||||
<i class="fa-solid fa-hand weapons-burden-icon left {{#if (or (eq weapons.burden "oneHanded") (eq weapons.burden "twoHanded"))}}active{{/if}}"></i>
|
<i class="fa-solid fa-hand weapons-burden-icon left {{#if (or (eq weaponBurden "oneHanded") (eq weaponBurden "twoHanded"))}}active{{/if}}"></i>
|
||||||
<i class="fa-solid fa-hand weapons-burden-icon right {{#if (eq weapons.burden "twoHanded")}}active{{/if}}"></i>
|
<i class="fa-solid fa-hand weapons-burden-icon right {{#if (eq weaponBurden "twoHanded")}}active{{/if}}"></i>
|
||||||
</div>
|
</div>
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
<div class="active-item-container">
|
<div class="active-item-container">
|
||||||
<h2 class="weapons-label-row">
|
<h2 class="weapons-label-row">
|
||||||
{{localize "DAGGERHEART.Sheets.PC.Weapons.PrimaryTitle"}}
|
{{localize "DAGGERHEART.Sheets.PC.Weapons.PrimaryTitle"}}
|
||||||
{{#if weapons.primary}}
|
{{#if primaryWeapon}}
|
||||||
<img class="damage-roll" data-action="attackRoll" data-weapon="{{weapons.primary.uuid}}" src="icons/svg/d12-grey.svg" />
|
<img class="damage-roll" data-action="attackRoll" data-weapon="{{primaryWeapon.uuid}}" src="icons/svg/d12-grey.svg" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<input value="{{weapons.primary.name}}" type="text" />
|
<input value="{{primaryWeapon.name}}" type="text" />
|
||||||
<input value="{{localize weapons.primary.trait}}" type="text" />
|
<input value="{{localize primaryWeapon.trait}}" type="text" />
|
||||||
<input value="{{localize weapons.primary.range.label}}" type="text" />
|
<input value="{{localize primaryWeapon.range.label}}" type="text" />
|
||||||
<input value="{{weapons.primary.damage.value}} {{#if weapons.primary}}({{localize weapons.primary.damage.type.abbreviation}}){{/if}}" type="text" />
|
<input value="{{primaryWeapon.damage.value}} {{#if primaryWeapon}}({{localize primaryWeapon.damage.type.abbreviation}}){{/if}}" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<input value="{{localize weapons.primary.feature.label}} {{#if weapons.primary.feature}}({{localize weapons.primary.feature.description}}){{/if}}" type="text" />
|
<input value="{{localize primaryWeapon.feature.label}} {{#if primaryWeapon.feature}}({{localize primaryWeapon.feature.description}}){{/if}}" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<div class="active-item-container">
|
<div class="active-item-container">
|
||||||
<h2 class="weapons-label-row">
|
<h2 class="weapons-label-row">
|
||||||
{{localize "DAGGERHEART.Sheets.PC.Weapons.SecondaryTitle"}}
|
{{localize "DAGGERHEART.Sheets.PC.Weapons.SecondaryTitle"}}
|
||||||
{{#if weapons.secondary}}
|
{{#if secondaryWeapon}}
|
||||||
<img class="damage-roll" data-action="damageRoll" data-value="{{weapons.secondary.damage.value}}" src="icons/svg/d12-grey.svg" />
|
<img class="damage-roll" data-action="damageRoll" data-value="{{secondaryWeapon.damage.value}}" src="icons/svg/d12-grey.svg" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</h2>
|
</h2>
|
||||||
<div class="flexrow">
|
<div class="flexrow">
|
||||||
<input value="{{weapons.secondary.name}}" type="text" />
|
<input value="{{secondaryWeapon.name}}" type="text" />
|
||||||
<input value="{{localize weapons.secondary.trait}}" type="text" />
|
<input value="{{localize secondaryWeapon.trait}}" type="text" />
|
||||||
<input value="{{localize weapons.secondary.range.label}}" type="text" />
|
<input value="{{localize secondaryWeapon.range.label}}" type="text" />
|
||||||
<input value="{{weapons.secondary.damage.label}} {{#if weapons.secondary}}({{localize weapons.secondary.damage.type.abbreviation}}){{/if}}" type="text" />
|
<input value="{{secondaryWeapon.damage.label}} {{#if secondaryWeapon}}({{localize secondaryWeapon.damage.type.abbreviation}}){{/if}}" type="text" />
|
||||||
</div>
|
</div>
|
||||||
<input value="{{localize weapons.secondary.feature.name}} {{#if weapons.secondary.feature}}({{localize weapons.secondary.feature.description}}){{/if}}" style="text-overflow: ellipsis;" type="text" />
|
<input value="{{localize secondaryWeapon.feature.name}} {{#if secondaryWeapon.feature}}({{localize secondaryWeapon.feature.description}}){{/if}}" style="text-overflow: ellipsis;" type="text" />
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
@ -15,22 +15,6 @@
|
||||||
<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="setAdvantage">Advantage</button>
|
||||||
<button class="disadvantage flex1 {{#if this.disadvantage}}selected{{/if}}" data-action="setDisadvantage">Disadvantage</button>
|
<button class="disadvantage flex1 {{#if this.disadvantage}}selected{{/if}}" data-action="setDisadvantage">Disadvantage</button>
|
||||||
</div>
|
</div>
|
||||||
{{!-- <div class="form-group">
|
|
||||||
<label>Advantage</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<select name="advantage" {{#if this.disadvantage}}disabled{{/if}}>
|
|
||||||
{{selectOptions this.diceOptions selected=this.advantage valueAttr="value" labelAttr="name" localize=true blank=""}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label>Disadvantage</label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<select name="disadvantage" {{#if this.advantage}}disabled{{/if}}>
|
|
||||||
{{selectOptions this.diceOptions selected=this.disadvantage valueAttr="value" labelAttr="name" localize=true blank=""}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div> --}}
|
|
||||||
{{#if (not this.isNpc)}}
|
{{#if (not this.isNpc)}}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Hope</label>
|
<label>Hope</label>
|
||||||
|
|
@ -49,24 +33,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#each this.bonusDamage as |damage index|}}
|
|
||||||
<div class="form-group">
|
|
||||||
<label><strong>{{damage.description}}</strong></label>
|
|
||||||
<div class="form-fields">
|
|
||||||
<label>Enabled</label>
|
|
||||||
<input style="align-self: baseline;" type="checkbox" name="bonusDamage.{{index}}.initiallySelected" {{checked damage.initiallySelected}} />
|
|
||||||
{{#if (and damage.initiallySelected damage.hopeIncrease)}}
|
|
||||||
|
|
||||||
<label>Hope</label>
|
|
||||||
<div class="hope-container">
|
|
||||||
<i data-action="decreaseHopeUse" data-index="{{index}}" class="fa-solid fa-caret-left icon-button {{#if (eq damage.hopeUses 0)}}disabled{{/if}}"></i>
|
|
||||||
<div>{{damage.hopeUses}}</div>
|
|
||||||
<i data-action="increaseHopeUse" data-index="{{index}}" class="fa-solid fa-caret-right icon-button {{#if (eq ../hopeUsed ../hopeResource)}}disabled{{/if}}"></i>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<button data-action="finish">Roll</button>
|
<button data-action="finish">Roll</button>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue