mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 07:36:26 +01:00
Merge branch 'main' into feature/Character-Setup-Rework
This commit is contained in:
commit
f751b2d757
63 changed files with 664 additions and 188 deletions
17
lang/en.json
17
lang/en.json
|
|
@ -402,6 +402,7 @@
|
|||
"default": "Default Ownership"
|
||||
}
|
||||
},
|
||||
|
||||
"CONFIG": {
|
||||
"ActionType": {
|
||||
"passive": "Passive",
|
||||
|
|
@ -690,6 +691,10 @@
|
|||
"name": "Dice Set"
|
||||
}
|
||||
},
|
||||
"SelectAction": {
|
||||
"selectType": "Select Action Type",
|
||||
"selectAction": "Select Action"
|
||||
},
|
||||
"Traits": {
|
||||
"agility": {
|
||||
"name": "Agility",
|
||||
|
|
@ -967,6 +972,10 @@
|
|||
"stress": {
|
||||
"name": "Stress"
|
||||
}
|
||||
},
|
||||
"Attachments": {
|
||||
"attachHint": "Drop items here to attach them",
|
||||
"transferHint": "If checked, this effect will be applied to any actor that owns this Effect's parent Item. The effect is always applied if this Item is attached to another one."
|
||||
}
|
||||
},
|
||||
"GENERAL": {
|
||||
|
|
@ -996,6 +1005,11 @@
|
|||
"minor": "Minor",
|
||||
"none": "None"
|
||||
},
|
||||
"DamageResistance": {
|
||||
"none": "None",
|
||||
"resistance": "Resistance",
|
||||
"immunity": "Immunity"
|
||||
},
|
||||
"DamageThresholds": {
|
||||
"title": "Damage Thresholds",
|
||||
"minor": "Minor",
|
||||
|
|
@ -1097,7 +1111,8 @@
|
|||
"optional": "Optional",
|
||||
"recovery": "Recovery",
|
||||
"setup": "Setup",
|
||||
"equipment": "Equipment"
|
||||
"equipment": "Equipment",
|
||||
"attachments": "Attachments"
|
||||
},
|
||||
"Tiers": {
|
||||
"singular": "Tier",
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export default class CostSelectionDialog extends HandlebarsApplicationMixin(Appl
|
|||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'damage-selection'],
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'damage-selection'],
|
||||
position: {
|
||||
width: 400,
|
||||
height: 'auto'
|
||||
|
|
|
|||
|
|
@ -11,11 +11,14 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
id: 'roll-selection',
|
||||
classes: ['daggerheart', 'views', 'damage-selection'],
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'damage-selection'],
|
||||
position: {
|
||||
width: 400,
|
||||
height: 'auto'
|
||||
},
|
||||
window: {
|
||||
icon: 'fa-solid fa-dice'
|
||||
},
|
||||
actions: {
|
||||
submitRoll: this.submitRoll
|
||||
},
|
||||
|
|
@ -34,9 +37,15 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
|
|||
}
|
||||
};
|
||||
|
||||
get title() {
|
||||
return game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||
}
|
||||
|
||||
async _prepareContext(_options) {
|
||||
const context = await super._prepareContext(_options);
|
||||
context.title = this.config.title;
|
||||
context.title = this.config.title
|
||||
? this.config.title
|
||||
: game.i18n.localize('DAGGERHEART.EFFECTS.ApplyLocations.damageRoll.name');
|
||||
context.extraFormula = this.config.extraFormula;
|
||||
context.formula = this.roll.constructFormula(this.config);
|
||||
return context;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
|
|||
this.actor = actor;
|
||||
this.damage = damage;
|
||||
|
||||
const canApplyArmor = actor.system.armorApplicableDamageTypes[damageType];
|
||||
const canApplyArmor = damageType.every(t => actor.system.armorApplicableDamageTypes[t] === true);
|
||||
const maxArmorMarks = canApplyArmor
|
||||
? Math.min(
|
||||
actor.system.armorScore - actor.system.armor.system.marks.value,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ export default class DamageSelectionDialog extends HandlebarsApplicationMixin(Ap
|
|||
|
||||
static DEFAULT_OPTIONS = {
|
||||
tag: 'form',
|
||||
classes: ['daggerheart', 'views', 'damage-selection'],
|
||||
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'damage-selection'],
|
||||
position: {
|
||||
width: 400,
|
||||
height: 'auto'
|
||||
|
|
|
|||
|
|
@ -56,10 +56,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
id: 'effect',
|
||||
template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs'
|
||||
}
|
||||
/* form: {
|
||||
id: 'action',
|
||||
template: 'systems/daggerheart/templates/config/action.hbs'
|
||||
} */
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
|
|
@ -161,7 +157,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
|
|||
container = foundry.utils.getProperty(this.action.parent, this.action.systemPath);
|
||||
let newActions;
|
||||
if (Array.isArray(container)) {
|
||||
newActions = foundry.utils.getProperty(this.action.parent, this.action.systemPath).map(x => x.toObject()); // Find better way
|
||||
newActions = foundry.utils.getProperty(this.action.parent, this.action.systemPath).map(x => x.toObject());
|
||||
if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data);
|
||||
} else newActions = data;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,12 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
window: {
|
||||
resizable: true
|
||||
},
|
||||
dragDrop: [],
|
||||
dragDrop: [
|
||||
{
|
||||
dragSelector: '[data-item-id][draggable="true"]',
|
||||
dropSelector: null
|
||||
}
|
||||
],
|
||||
contextMenus: [
|
||||
{
|
||||
handler: CharacterSheet._getContextMenuOptions,
|
||||
|
|
@ -665,11 +670,24 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
|||
}
|
||||
}
|
||||
|
||||
async _onDragStart(_, event) {
|
||||
async _onDragStart(event) {
|
||||
const item = this.getItem(event);
|
||||
|
||||
const dragData = {
|
||||
type: item.documentName,
|
||||
uuid: item.uuid
|
||||
};
|
||||
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(dragData));
|
||||
|
||||
super._onDragStart(event);
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
// Prevent event bubbling to avoid duplicate handling
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
super._onDrop(event);
|
||||
this._onDropItem(event, TextEditor.getDragEventData(event));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export { default as DHApplicationMixin } from './application-mixin.mjs';
|
||||
export { default as DHBaseItemSheet } from './base-item.mjs';
|
||||
export { default as DHHeritageSheet } from './heritage-sheet.mjs';
|
||||
export { default as DHItemAttachmentSheet } from './item-attachment-sheet.mjs';
|
||||
export { default as DHBaseActorSheet } from './base-actor.mjs';
|
||||
export { default as DHBaseActorSettings } from './actor-setting.mjs';
|
||||
|
|
|
|||
90
module/applications/sheets/api/item-attachment-sheet.mjs
Normal file
90
module/applications/sheets/api/item-attachment-sheet.mjs
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
|
||||
export default function ItemAttachmentSheet(Base) {
|
||||
return class extends Base {
|
||||
static DEFAULT_OPTIONS = {
|
||||
...super.DEFAULT_OPTIONS,
|
||||
dragDrop: [
|
||||
...(super.DEFAULT_OPTIONS.dragDrop || []),
|
||||
{ dragSelector: null, dropSelector: '.attachments-section' }
|
||||
],
|
||||
actions: {
|
||||
...super.DEFAULT_OPTIONS.actions,
|
||||
removeAttachment: this.#removeAttachment
|
||||
}
|
||||
};
|
||||
|
||||
static PARTS = {
|
||||
...super.PARTS,
|
||||
attachments: {
|
||||
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-attachments.hbs',
|
||||
scrollable: ['.attachments']
|
||||
}
|
||||
};
|
||||
|
||||
static TABS = {
|
||||
...super.TABS,
|
||||
primary: {
|
||||
...super.TABS?.primary,
|
||||
tabs: [
|
||||
...(super.TABS?.primary?.tabs || []),
|
||||
{ id: 'attachments' }
|
||||
],
|
||||
initial: super.TABS?.primary?.initial || 'description',
|
||||
labelPrefix: super.TABS?.primary?.labelPrefix || 'DAGGERHEART.GENERAL.Tabs'
|
||||
}
|
||||
};
|
||||
|
||||
async _preparePartContext(partId, context) {
|
||||
await super._preparePartContext(partId, context);
|
||||
|
||||
if (partId === 'attachments') {
|
||||
context.attachedItems = await prepareAttachmentContext(this.document);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async _onDrop(event) {
|
||||
const data = TextEditor.getDragEventData(event);
|
||||
|
||||
const attachmentsSection = event.target.closest('.attachments-section');
|
||||
if (!attachmentsSection) return super._onDrop(event);
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const item = await Item.implementation.fromDropData(data);
|
||||
if (!item) return;
|
||||
|
||||
// Call the data model's public method
|
||||
await this.document.system.addAttachment(item);
|
||||
}
|
||||
|
||||
|
||||
static async #removeAttachment(event, target) {
|
||||
// Call the data model's public method
|
||||
await this.document.system.removeAttachment(target.dataset.uuid);
|
||||
}
|
||||
|
||||
async _preparePartContext(partId, context) {
|
||||
await super._preparePartContext(partId, context);
|
||||
|
||||
if (partId === 'attachments') {
|
||||
// Keep this simple UI preparation in the mixin
|
||||
const attachedUUIDs = this.document.system.attached;
|
||||
context.attachedItems = await Promise.all(
|
||||
attachedUUIDs.map(async uuid => {
|
||||
const item = await fromUuid(uuid);
|
||||
return {
|
||||
uuid: uuid,
|
||||
name: item?.name || 'Unknown Item',
|
||||
img: item?.img || 'icons/svg/item-bag.svg'
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import DHBaseItemSheet from '../api/base-item.mjs';
|
||||
import ItemAttachmentSheet from '../api/item-attachment-sheet.mjs';
|
||||
|
||||
export default class ArmorSheet extends DHBaseItemSheet {
|
||||
export default class ArmorSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['armor'],
|
||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
||||
tagifyConfigs: [
|
||||
{
|
||||
selector: '.features-input',
|
||||
|
|
@ -26,7 +26,8 @@ export default class ArmorSheet extends DHBaseItemSheet {
|
|||
settings: {
|
||||
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
||||
scrollable: ['.settings']
|
||||
}
|
||||
},
|
||||
...super.PARTS,
|
||||
};
|
||||
|
||||
/**@inheritdoc */
|
||||
|
|
|
|||
|
|
@ -61,12 +61,17 @@ export default class FeatureSheet extends DHBaseItemSheet {
|
|||
static async selectActionType() {
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/actionTypes/actionType.hbs',
|
||||
{ types: CONFIG.DH.ACTIONS.actionTypes }
|
||||
{
|
||||
types: CONFIG.DH.ACTIONS.actionTypes,
|
||||
itemName: game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType')
|
||||
}
|
||||
),
|
||||
title = 'Select Action Type';
|
||||
title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectType');
|
||||
console.log(this.document);
|
||||
|
||||
return foundry.applications.api.DialogV2.prompt({
|
||||
window: { title },
|
||||
classes: ['daggerheart', 'dh-style'],
|
||||
content,
|
||||
ok: {
|
||||
label: title,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import DHBaseItemSheet from '../api/base-item.mjs';
|
||||
import ItemAttachmentSheet from '../api/item-attachment-sheet.mjs';
|
||||
|
||||
export default class WeaponSheet extends DHBaseItemSheet {
|
||||
export default class WeaponSheet extends ItemAttachmentSheet(DHBaseItemSheet) {
|
||||
/**@inheritdoc */
|
||||
static DEFAULT_OPTIONS = {
|
||||
classes: ['weapon'],
|
||||
|
|
@ -25,12 +26,13 @@ export default class WeaponSheet extends DHBaseItemSheet {
|
|||
settings: {
|
||||
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
||||
scrollable: ['.settings']
|
||||
}
|
||||
},
|
||||
...super.PARTS,
|
||||
};
|
||||
|
||||
/**@inheritdoc */
|
||||
async _preparePartContext(partId, context) {
|
||||
super._preparePartContext(partId, context);
|
||||
await super._preparePartContext(partId, context);
|
||||
switch (partId) {
|
||||
case 'settings':
|
||||
context.features = this.document.system.weaponFeatures.map(x => x.value);
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
|
|||
if (message.system.onSave && message.system.targets.find(t => t.id === target.id)?.saved?.success === true)
|
||||
damage = Math.ceil(damage * (CONFIG.DH.ACTIONS.damageOnSave[message.system.onSave]?.mod ?? 1));
|
||||
|
||||
target.actor.takeDamage(damage, message.system.roll.type);
|
||||
target.actor.takeDamage(damage, message.system.damage.damageType);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,3 +7,5 @@ export const encounterCountdown = {
|
|||
simple: 'countdown-encounter-simple',
|
||||
position: 'countdown-encounter-position'
|
||||
};
|
||||
|
||||
export const itemAttachmentSource = 'attachmentSource';
|
||||
|
|
@ -59,13 +59,13 @@ export const damageTypes = {
|
|||
id: 'physical',
|
||||
label: 'DAGGERHEART.CONFIG.DamageType.physical.name',
|
||||
abbreviation: 'DAGGERHEART.CONFIG.DamageType.physical.abbreviation',
|
||||
icon: ['fa-hand-fist']
|
||||
icon: 'fa-hand-fist'
|
||||
},
|
||||
magical: {
|
||||
id: 'magical',
|
||||
label: 'DAGGERHEART.CONFIG.DamageType.magical.name',
|
||||
abbreviation: 'DAGGERHEART.CONFIG.DamageType.magical.abbreviation',
|
||||
icon: ['fa-wand-sparkles']
|
||||
icon: 'fa-wand-sparkles'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -395,7 +395,7 @@ export const armorFeatures = {
|
|||
img: 'icons/magic/defensive/barrier-shield-dome-pink.webp',
|
||||
changes: [
|
||||
{
|
||||
key: 'system.bonuses.damageReduction.magical',
|
||||
key: 'system.resistance.magical.reduction',
|
||||
mode: 2,
|
||||
value: '@system.armorScore'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,11 +76,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
|
|||
};
|
||||
}
|
||||
|
||||
getFormula(actor) {
|
||||
/* const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : actor.system[this.multiplier]?.total;
|
||||
return this.custom.enabled
|
||||
? this.custom.formula
|
||||
: `${multiplier ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`; */
|
||||
getFormula() {
|
||||
const multiplier = this.multiplier === 'flat' ? this.flatMultiplier : `@${this.multiplier}`,
|
||||
bonus = this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : '';
|
||||
return this.custom.enabled ? this.custom.formula : `${multiplier ?? 1}${this.dice}${bonus}`;
|
||||
|
|
@ -93,7 +89,6 @@ export class DHDamageField extends fields.SchemaField {
|
|||
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData)),
|
||||
includeBase: new fields.BooleanField({ initial: false })
|
||||
};
|
||||
// if (hasBase) damageFields.includeBase = new fields.BooleanField({ initial: true });
|
||||
super(damageFields, options, context);
|
||||
}
|
||||
}
|
||||
|
|
@ -102,15 +97,19 @@ export class DHDamageData extends foundry.abstract.DataModel {
|
|||
/** @override */
|
||||
static defineSchema() {
|
||||
return {
|
||||
// ...super.defineSchema(),
|
||||
base: new fields.BooleanField({ initial: false, readonly: true, label: 'Base' }),
|
||||
type: new fields.StringField({
|
||||
type: new fields.SetField(
|
||||
new fields.StringField({
|
||||
choices: CONFIG.DH.GENERAL.damageTypes,
|
||||
initial: 'physical',
|
||||
label: 'Type',
|
||||
nullable: false,
|
||||
required: true
|
||||
}),
|
||||
{
|
||||
label: 'Type',
|
||||
initial: 'physical',
|
||||
}
|
||||
),
|
||||
resultBased: new fields.BooleanField({
|
||||
initial: false,
|
||||
label: 'DAGGERHEART.ACTIONS.Settings.resultBased.label'
|
||||
|
|
|
|||
|
|
@ -179,16 +179,9 @@ export default class DHBaseAction extends foundry.abstract.DataModel {
|
|||
getRollData(data = {}) {
|
||||
const actorData = this.actor.getRollData(false);
|
||||
|
||||
// Remove when included directly in Actor getRollData
|
||||
actorData.prof = actorData.proficiency?.total ?? 1;
|
||||
actorData.cast = actorData.spellcast?.total ?? 1;
|
||||
// Add Roll results to RollDatas
|
||||
actorData.result = data.roll?.total ?? 1;
|
||||
/* actorData.scale = data.costs?.length
|
||||
? data.costs.reduce((a, c) => {
|
||||
a[c.type] = c.value;
|
||||
return a;
|
||||
}, {})
|
||||
: 1; */
|
||||
|
||||
actorData.scale = data.costs?.length // Right now only return the first scalable cost.
|
||||
? (data.costs.find(c => c.scalable)?.total ?? 1)
|
||||
: 1;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
}
|
||||
|
||||
async rollDamage(event, data) {
|
||||
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + ');
|
||||
let formula = this.damage.parts.map(p => this.getFormulaValue(p, data).getFormula(this.actor)).join(' + '),
|
||||
damageTypes = [...new Set(this.damage.parts.reduce((a,c) => a.concat([...c.type]), []))];
|
||||
|
||||
damageTypes = !damageTypes.length ? ['physical'] : damageTypes;
|
||||
|
||||
if (!formula || formula == '') return;
|
||||
let roll = { formula: formula, total: formula },
|
||||
|
|
@ -25,6 +28,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
hasSave: this.hasSave,
|
||||
isCritical: data.system?.roll?.isCritical ?? false,
|
||||
source: data.system?.source,
|
||||
damageTypes,
|
||||
event
|
||||
};
|
||||
if (this.hasSave) config.onSave = this.save.damageMod;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import BaseDataActor from './base.mjs';
|
|||
const resourceField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
bonus: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
max: new foundry.data.fields.NumberField({ initial: 0, integer: true })
|
||||
});
|
||||
|
||||
|
|
@ -22,6 +23,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
tier: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
|
|
@ -32,7 +34,6 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
choices: CONFIG.DH.ACTOR.adversaryTypes,
|
||||
initial: CONFIG.DH.ACTOR.adversaryTypes.standard.id
|
||||
}),
|
||||
description: new fields.StringField(),
|
||||
motivesAndTactics: new fields.StringField(),
|
||||
notes: new fields.HTMLField(),
|
||||
difficulty: new fields.NumberField({ required: true, initial: 1, integer: true }),
|
||||
|
|
@ -63,6 +64,7 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
damage: {
|
||||
parts: [
|
||||
{
|
||||
type: ['physical'],
|
||||
value: {
|
||||
multiplier: 'flat'
|
||||
}
|
||||
|
|
@ -93,4 +95,9 @@ export default class DhpAdversary extends BaseDataActor {
|
|||
get features() {
|
||||
return this.parent.items.filter(x => x.type === 'feature');
|
||||
}
|
||||
|
||||
prepareDerivedData() {
|
||||
this.resources.hitPoints.maxTotal = this.resources.hitPoints.max + this.resources.hitPoints.bonus;
|
||||
this.resources.stress.maxTotal = this.resources.stress.max + this.resources.stress.bonus;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,12 @@
|
|||
import DHBaseActorSettings from "../../applications/sheets/api/actor-setting.mjs";
|
||||
|
||||
const resistanceField = () =>
|
||||
new foundry.data.fields.SchemaField({
|
||||
resistance: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
immunity: new foundry.data.fields.BooleanField({ initial: false }),
|
||||
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0 })
|
||||
});
|
||||
|
||||
/**
|
||||
* Describes metadata about the actor data model type
|
||||
* @typedef {Object} ActorDataModelMetadata
|
||||
|
|
@ -16,6 +23,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
type: 'base',
|
||||
isNPC: true,
|
||||
settingSheet: null,
|
||||
hasResistances: true
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -27,10 +35,16 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
|
|||
/** @inheritDoc */
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
const schema = {};
|
||||
|
||||
return {
|
||||
description: new fields.HTMLField({ required: true, nullable: true })
|
||||
};
|
||||
if(this.metadata.isNPC)
|
||||
schema.description = new fields.HTMLField({ required: true, nullable: true });
|
||||
if(this.metadata.hasResistances)
|
||||
schema.resistance = new fields.SchemaField({
|
||||
physical: resistanceField(),
|
||||
magical: resistanceField()
|
||||
})
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export default class DhCharacter extends BaseDataActor {
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
resources: new fields.SchemaField({
|
||||
hitPoints: new fields.SchemaField({
|
||||
value: new foundry.data.fields.NumberField({ initial: 0, integer: true }),
|
||||
|
|
@ -100,10 +101,6 @@ export default class DhCharacter extends BaseDataActor {
|
|||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
armorScore: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
damageReduction: new fields.SchemaField({
|
||||
physical: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
magical: new fields.NumberField({ integer: true, initial: 0 })
|
||||
}),
|
||||
damageThresholds: new fields.SchemaField({
|
||||
severe: new fields.NumberField({ integer: true, initial: 0 }),
|
||||
major: new fields.NumberField({ integer: true, initial: 0 })
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export default class DhCompanion extends BaseDataActor {
|
|||
const fields = foundry.data.fields;
|
||||
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
partner: new ForeignDocumentUUIDField({ type: 'Actor' }),
|
||||
resources: new fields.SchemaField({
|
||||
stress: new fields.SchemaField({
|
||||
|
|
@ -66,6 +67,7 @@ export default class DhCompanion extends BaseDataActor {
|
|||
damage: {
|
||||
parts: [
|
||||
{
|
||||
type: ['physical'],
|
||||
value: {
|
||||
dice: 'd6',
|
||||
multiplier: 'prof'
|
||||
|
|
|
|||
|
|
@ -9,20 +9,21 @@ export default class DhEnvironment extends BaseDataActor {
|
|||
return foundry.utils.mergeObject(super.metadata, {
|
||||
label: 'TYPES.Actor.environment',
|
||||
type: 'environment',
|
||||
settingSheet: DHEnvironmentSettings
|
||||
settingSheet: DHEnvironmentSettings,
|
||||
hasResistances: false
|
||||
});
|
||||
}
|
||||
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
tier: new fields.StringField({
|
||||
required: true,
|
||||
choices: CONFIG.DH.GENERAL.tiers,
|
||||
initial: CONFIG.DH.GENERAL.tiers.tier1.id
|
||||
}),
|
||||
type: new fields.StringField({ choices: CONFIG.DH.ACTOR.environmentTypes }),
|
||||
description: new fields.StringField(),
|
||||
impulses: new fields.StringField(),
|
||||
difficulty: new fields.NumberField({ required: true, initial: 11, integer: true }),
|
||||
potentialAdversaries: new fields.TypedObjectField(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import DHAncestry from './ancestry.mjs';
|
||||
import DHArmor from './armor.mjs';
|
||||
import DHAttachableItem from './attachableItem.mjs';
|
||||
import DHClass from './class.mjs';
|
||||
import DHCommunity from './community.mjs';
|
||||
import DHConsumable from './consumable.mjs';
|
||||
|
|
@ -13,6 +14,7 @@ import DHBeastform from './beastform.mjs';
|
|||
export {
|
||||
DHAncestry,
|
||||
DHArmor,
|
||||
DHAttachableItem,
|
||||
DHClass,
|
||||
DHCommunity,
|
||||
DHConsumable,
|
||||
|
|
@ -27,6 +29,7 @@ export {
|
|||
export const config = {
|
||||
ancestry: DHAncestry,
|
||||
armor: DHArmor,
|
||||
attachableItem: DHAttachableItem,
|
||||
class: DHClass,
|
||||
community: DHCommunity,
|
||||
consumable: DHConsumable,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
import AttachableItem from './attachableItem.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
import { armorFeatures } from '../../config/itemConfig.mjs';
|
||||
import { actionsTypes } from '../action/_module.mjs';
|
||||
|
||||
export default class DHArmor extends BaseDataItem {
|
||||
export default class DHArmor extends AttachableItem {
|
||||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
|
|
|
|||
152
module/data/item/attachableItem.mjs
Normal file
152
module/data/item/attachableItem.mjs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
|
||||
export default class AttachableItem extends BaseDataItem {
|
||||
static defineSchema() {
|
||||
const fields = foundry.data.fields;
|
||||
return {
|
||||
...super.defineSchema(),
|
||||
attached: new fields.ArrayField(new fields.DocumentUUIDField({ type: "Item", nullable: true }))
|
||||
};
|
||||
}
|
||||
|
||||
async _preUpdate(changes, options, user) {
|
||||
const allowed = await super._preUpdate(changes, options, user);
|
||||
if (allowed === false) return false;
|
||||
|
||||
// Handle equipped status changes for attachment effects
|
||||
if (changes.system?.equipped !== undefined && changes.system.equipped !== this.equipped) {
|
||||
await this.#handleAttachmentEffectsOnEquipChange(changes.system.equipped);
|
||||
}
|
||||
}
|
||||
|
||||
async #handleAttachmentEffectsOnEquipChange(newEquippedStatus) {
|
||||
const actor = this.parent.parent?.type === 'character' ? this.parent.parent : this.parent.parent?.parent;
|
||||
const parentType = this.parent.type;
|
||||
|
||||
if (!actor || !this.attached?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newEquippedStatus) {
|
||||
// Item is being equipped - add attachment effects
|
||||
for (const attachedUuid of this.attached) {
|
||||
const attachedItem = await fromUuid(attachedUuid);
|
||||
if (attachedItem && attachedItem.effects.size > 0) {
|
||||
await this.#copyAttachmentEffectsToActor({
|
||||
attachedItem,
|
||||
attachedUuid,
|
||||
parentType
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Item is being unequipped - remove attachment effects
|
||||
await this.#removeAllAttachmentEffects(parentType);
|
||||
}
|
||||
}
|
||||
|
||||
async #copyAttachmentEffectsToActor({ attachedItem, attachedUuid, parentType }) {
|
||||
const actor = this.parent.parent;
|
||||
if (!actor || !attachedItem.effects.size > 0 || !this.equipped) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const effectsToCreate = [];
|
||||
for (const effect of attachedItem.effects) {
|
||||
const effectData = effect.toObject();
|
||||
effectData.origin = `${this.parent.uuid}:${attachedUuid}`;
|
||||
|
||||
const attachmentSource = {
|
||||
itemUuid: attachedUuid,
|
||||
originalEffectId: effect.id
|
||||
};
|
||||
attachmentSource[`${parentType}Uuid`] = this.parent.uuid;
|
||||
|
||||
effectData.flags = {
|
||||
...effectData.flags,
|
||||
[CONFIG.DH.id]: {
|
||||
...effectData.flags?.[CONFIG.DH.id],
|
||||
[CONFIG.DH.FLAGS.itemAttachmentSource]: attachmentSource
|
||||
}
|
||||
};
|
||||
effectsToCreate.push(effectData);
|
||||
}
|
||||
|
||||
if (effectsToCreate.length > 0) {
|
||||
return await actor.createEmbeddedDocuments('ActiveEffect', effectsToCreate);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
async #removeAllAttachmentEffects(parentType) {
|
||||
const actor = this.parent.parent;
|
||||
if (!actor) return;
|
||||
|
||||
const parentUuidProperty = `${parentType}Uuid`;
|
||||
const effectsToRemove = actor.effects.filter(effect => {
|
||||
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
|
||||
return attachmentSource && attachmentSource[parentUuidProperty] === this.parent.uuid;
|
||||
});
|
||||
|
||||
if (effectsToRemove.length > 0) {
|
||||
await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method for adding an attachment
|
||||
*/
|
||||
async addAttachment(droppedItem) {
|
||||
const newUUID = droppedItem.uuid;
|
||||
|
||||
if (this.attached.includes(newUUID)) {
|
||||
ui.notifications.warn(`${droppedItem.name} is already attached to this ${this.parent.type}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedAttached = [...this.attached, newUUID];
|
||||
await this.parent.update({
|
||||
'system.attached': updatedAttached
|
||||
});
|
||||
|
||||
// Copy effects if equipped
|
||||
if (this.equipped && droppedItem.effects.size > 0) {
|
||||
await this.#copyAttachmentEffectsToActor({
|
||||
attachedItem: droppedItem,
|
||||
attachedUuid: newUUID,
|
||||
parentType: this.parent.type
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Public method for removing an attachment
|
||||
*/
|
||||
async removeAttachment(attachedUuid) {
|
||||
await this.parent.update({
|
||||
'system.attached': this.attached.filter(uuid => uuid !== attachedUuid)
|
||||
});
|
||||
|
||||
// Remove effects
|
||||
await this.#removeAttachmentEffects(attachedUuid);
|
||||
}
|
||||
|
||||
async #removeAttachmentEffects(attachedUuid) {
|
||||
const actor = this.parent.parent;
|
||||
if (!actor) return;
|
||||
|
||||
const parentType = this.parent.type;
|
||||
const parentUuidProperty = `${parentType}Uuid`;
|
||||
const effectsToRemove = actor.effects.filter(effect => {
|
||||
const attachmentSource = effect.getFlag(CONFIG.DH.id, CONFIG.DH.FLAGS.itemAttachmentSource);
|
||||
return attachmentSource &&
|
||||
attachmentSource[parentUuidProperty] === this.parent.uuid &&
|
||||
attachmentSource.itemUuid === attachedUuid;
|
||||
});
|
||||
|
||||
if (effectsToRemove.length > 0) {
|
||||
await actor.deleteEmbeddedDocuments('ActiveEffect', effectsToRemove.map(e => e.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import BaseDataItem from './base.mjs';
|
||||
import AttachableItem from './attachableItem.mjs';
|
||||
import { actionsTypes } from '../action/_module.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
|
||||
export default class DHWeapon extends BaseDataItem {
|
||||
export default class DHWeapon extends AttachableItem {
|
||||
/** @inheritDoc */
|
||||
static get metadata() {
|
||||
return foundry.utils.mergeObject(super.metadata, {
|
||||
|
|
@ -56,6 +56,7 @@ export default class DHWeapon extends BaseDataItem {
|
|||
damage: {
|
||||
parts: [
|
||||
{
|
||||
type: ['physical'],
|
||||
value: {
|
||||
multiplier: 'prof',
|
||||
dice: 'd8'
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@ export default class DamageRoll extends DHRoll {
|
|||
super.postEvaluate(roll, config);
|
||||
config.roll.type = config.type;
|
||||
config.roll.modifierTotal = this.calculateTotalModifiers(roll);
|
||||
}
|
||||
|
||||
static async buildPost(roll, config, message) {
|
||||
await super.buildPost(roll, config, message);
|
||||
if (config.source?.message) {
|
||||
const chatMessage = ui.chat.collection.get(config.source.message);
|
||||
chatMessage.update({ 'system.damage': config });
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ export default class DHRoll extends Roll {
|
|||
|
||||
// Create Chat Message
|
||||
if (config.source?.message) {
|
||||
if(game.modules.get('dice-so-nice')?.active) await game.dice3d.showForRoll(roll, game.user, true);
|
||||
} else {
|
||||
const messageData = {};
|
||||
config.message = await this.toMessage(roll, config);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,42 @@
|
|||
export default class DhActiveEffect extends ActiveEffect {
|
||||
get isSuppressed() {
|
||||
if (['weapon', 'armor'].includes(this.parent.type)) {
|
||||
// If this is a copied effect from an attachment, never suppress it
|
||||
// (These effects have attachmentSource metadata)
|
||||
if (this.flags?.daggerheart?.attachmentSource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then apply the standard suppression rules
|
||||
if (['weapon', 'armor'].includes(this.parent?.type)) {
|
||||
return !this.parent.system.equipped;
|
||||
}
|
||||
|
||||
if (this.parent.type === 'domainCard') {
|
||||
if (this.parent?.type === 'domainCard') {
|
||||
return this.parent.system.inVault;
|
||||
}
|
||||
|
||||
return super.isSuppressed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parent item is currently attached to another item
|
||||
* @returns {boolean}
|
||||
*/
|
||||
get isAttached() {
|
||||
if (!this.parent || !this.parent.parent) return false;
|
||||
|
||||
// Check if this item's UUID is in any actor's armor or weapon attachment lists
|
||||
const actor = this.parent.parent;
|
||||
if (!actor || !actor.items) return false;
|
||||
|
||||
return actor.items.some(item => {
|
||||
return (item.type === 'armor' || item.type === 'weapon') &&
|
||||
item.system?.attached &&
|
||||
Array.isArray(item.system.attached) &&
|
||||
item.system.attached.includes(this.parent.uuid);
|
||||
});
|
||||
}
|
||||
|
||||
async _preCreate(data, options, user) {
|
||||
const update = {};
|
||||
if (!data.img) {
|
||||
|
|
|
|||
|
|
@ -370,7 +370,10 @@ export default class DhpActor extends Actor {
|
|||
}
|
||||
|
||||
getRollData() {
|
||||
return this.system;
|
||||
const rollData = super.getRollData();
|
||||
rollData.prof = this.system.proficiency?.total ?? 1;
|
||||
rollData.cast = this.system.spellcast?.total ?? 1;
|
||||
return rollData;
|
||||
}
|
||||
|
||||
formatRollModifier(roll) {
|
||||
|
|
@ -462,7 +465,7 @@ export default class DhpActor extends Actor {
|
|||
const canUseArmor =
|
||||
this.system.armor &&
|
||||
this.system.armor.system.marks.value < this.system.armorScore &&
|
||||
this.system.armorApplicableDamageTypes[type];
|
||||
type.every(t => this.system.armorApplicableDamageTypes[t] === true);
|
||||
const canUseStress = Object.keys(this.system.rules.damageReduction.stressDamageReduction).reduce((acc, x) => {
|
||||
const rule = this.system.rules.damageReduction.stressDamageReduction[x];
|
||||
if (damageKeyToNumber(x) <= hpDamage) return acc || (rule.enabled && availableStress >= rule.cost);
|
||||
|
|
@ -480,11 +483,9 @@ export default class DhpActor extends Actor {
|
|||
return;
|
||||
}
|
||||
|
||||
const flatReduction = this.system.bonuses.damageReduction[type];
|
||||
const damage = Math.max(baseDamage - (flatReduction ?? 0), 0);
|
||||
const hpDamage = this.convertDamageToThreshold(damage);
|
||||
type = !Array.isArray(type) ? [type] : type;
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postDamageTreshold`, this, hpDamage, damage, type) === false) return null;
|
||||
const hpDamage = this.calculateDamage(baseDamage, type);
|
||||
|
||||
if (!hpDamage) return;
|
||||
|
||||
|
|
@ -511,6 +512,35 @@ export default class DhpActor extends Actor {
|
|||
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, damage, type) === false) return null;
|
||||
}
|
||||
|
||||
calculateDamage(baseDamage, type) {
|
||||
if (Hooks.call(`${CONFIG.DH.id}.preCalculateDamage`, this, baseDamage, type) === false) return null;
|
||||
|
||||
/* if(this.system.resistance[type]?.immunity) return 0;
|
||||
if(this.system.resistance[type]?.resistance) baseDamage = Math.ceil(baseDamage / 2); */
|
||||
if(this.canResist(type, 'immunity')) return 0;
|
||||
if(this.canResist(type, 'resistance')) baseDamage = Math.ceil(baseDamage / 2);
|
||||
|
||||
// const flatReduction = this.system.resistance[type].reduction;
|
||||
const flatReduction = this.getDamageTypeReduction(type);
|
||||
const damage = Math.max(baseDamage - (flatReduction ?? 0), 0);
|
||||
const hpDamage = this.convertDamageToThreshold(damage);
|
||||
|
||||
if (Hooks.call(`${CONFIG.DH.id}.postCalculateDamage`, this, baseDamage, type) === false) return null;
|
||||
|
||||
return hpDamage;
|
||||
}
|
||||
|
||||
canResist(type, resistance) {
|
||||
if(!type) return 0;
|
||||
return type.every(t => this.system.resistance[t]?.[resistance] === true);
|
||||
}
|
||||
|
||||
getDamageTypeReduction(type) {
|
||||
if(!type) return 0;
|
||||
const reduction = Object.entries(this.system.resistance).reduce((a, [index, value]) => type.includes(index) ? Math.min(value.reduction, a) : a, Infinity);
|
||||
return reduction === Infinity ? 0 : reduction;
|
||||
}
|
||||
|
||||
async takeHealing(resources) {
|
||||
resources.forEach(r => (r.value *= -1));
|
||||
await this.modifyResource(resources);
|
||||
|
|
@ -553,18 +583,6 @@ export default class DhpActor extends Actor {
|
|||
u.resources,
|
||||
u.target.uuid
|
||||
);
|
||||
/* if (game.user.isGM) {
|
||||
await u.target.update(u.resources);
|
||||
} else {
|
||||
await game.socket.emit(`system.${CONFIG.DH.id}`, {
|
||||
action: socketEvent.GMUpdate,
|
||||
data: {
|
||||
action: GMUpdateEvent.UpdateDocument,
|
||||
uuid: u.target.uuid,
|
||||
update: u.resources
|
||||
}
|
||||
});
|
||||
} */
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,12 +80,16 @@ export default class DHItem extends foundry.documents.Item {
|
|||
async selectActionDialog(prevEvent) {
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
'systems/daggerheart/templates/dialogs/actionSelect.hbs',
|
||||
{ actions: this.system.actionsList }
|
||||
{
|
||||
actions: this.system.actionsList,
|
||||
itemName: this.name
|
||||
}
|
||||
),
|
||||
title = 'Select Action';
|
||||
title = game.i18n.localize('DAGGERHEART.CONFIG.SelectAction.selectAction');
|
||||
|
||||
return foundry.applications.api.DialogV2.prompt({
|
||||
window: { title },
|
||||
classes: ['daggerheart', 'dh-style'],
|
||||
content,
|
||||
ok: {
|
||||
label: title,
|
||||
|
|
|
|||
|
|
@ -236,16 +236,7 @@ Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false
|
|||
};
|
||||
|
||||
export const getDamageKey = damage => {
|
||||
switch (damage) {
|
||||
case 3:
|
||||
return 'severe';
|
||||
case 2:
|
||||
return 'major';
|
||||
case 1:
|
||||
return 'minor';
|
||||
case 0:
|
||||
return 'none';
|
||||
}
|
||||
return ['none', 'minor', 'major', 'severe'][damage];
|
||||
};
|
||||
|
||||
export const getDamageLabel = damage => {
|
||||
|
|
@ -253,16 +244,12 @@ export const getDamageLabel = damage => {
|
|||
};
|
||||
|
||||
export const damageKeyToNumber = key => {
|
||||
switch (key) {
|
||||
case 'severe':
|
||||
return 3;
|
||||
case 'major':
|
||||
return 2;
|
||||
case 'minor':
|
||||
return 1;
|
||||
case 'none':
|
||||
return 0;
|
||||
}
|
||||
return {
|
||||
'none': 0,
|
||||
'minor': 1,
|
||||
'major': 2,
|
||||
'severe': 3
|
||||
}[key];
|
||||
};
|
||||
|
||||
export default function constructHTMLButton({
|
||||
|
|
|
|||
19
styles/less/dialog/actions/action-list.less
Normal file
19
styles/less/dialog/actions/action-list.less
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
@import '../../utils/fonts.less';
|
||||
|
||||
.application.daggerheart.dh-style {
|
||||
.actions-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
.action-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
|
||||
.label {
|
||||
font-family: @font-body;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
20
styles/less/dialog/damage-selection/sheet.less
Normal file
20
styles/less/dialog/damage-selection/sheet.less
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
@import '../../utils/colors.less';
|
||||
|
||||
.daggerheart.dialog.dh-style.views.damage-selection {
|
||||
.damage-section-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
input[type='text'],
|
||||
input[type='number'] {
|
||||
color: light-dark(@dark, @beige);
|
||||
outline: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
outline: 2px solid light-dark(@dark, @beige);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -114,15 +114,5 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.formula-label {
|
||||
font-family: @font-body;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
|
||||
color: light-dark(@dark, @beige);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
@import './level-up/summary-container.less';
|
||||
@import './level-up/tiers-container.less';
|
||||
|
||||
@import './actions/action-list.less';
|
||||
|
||||
@import './damage-selection/sheet.less';
|
||||
|
||||
@import './downtime/downtime-container.less';
|
||||
|
||||
@import './beastform/beastform-container.less';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
@import '../utils/colors.less';
|
||||
@import '../utils/fonts.less';
|
||||
@import '../utils/mixin.less';
|
||||
|
||||
.appTheme({
|
||||
&.dialog {
|
||||
|
|
@ -40,4 +41,19 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.formula-label {
|
||||
font-family: @font-body;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
|
||||
color: light-dark(@dark, @beige);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,15 +35,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
input[type='checkbox'],
|
||||
input[type='radio'] {
|
||||
&:checked::after {
|
||||
color: light-dark(@dark, @golden);
|
||||
color: light-dark(@dark-40, @golden);
|
||||
}
|
||||
&:checked::before {
|
||||
color: light-dark(transparent, @dark-blue);
|
||||
color: light-dark(@dark-40, @golden-40);
|
||||
}
|
||||
&::before {
|
||||
color: light-dark(@dark, @beige);
|
||||
color: light-dark(@dark-40, @golden-40);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -103,6 +104,40 @@
|
|||
}
|
||||
}
|
||||
|
||||
multi-select {
|
||||
position: relative;
|
||||
height: 34px;
|
||||
.tags {
|
||||
justify-content: flex-start;
|
||||
margin: 5px;
|
||||
height: inherit;
|
||||
.tag {
|
||||
box-shadow: 0 0 0 1.1em #E5E5E5 inset;
|
||||
vertical-align: top;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
padding: 0.3em 0 0.3em 0.5em;
|
||||
color: black;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
transition: .13s ease-out;
|
||||
height: 22px;
|
||||
font-size: .9rem;
|
||||
gap: 0.5em;
|
||||
z-index: 1;
|
||||
.remove {
|
||||
font-size: 10px;
|
||||
margin-inline: auto 4.6666666667px;
|
||||
}
|
||||
}
|
||||
}
|
||||
select {
|
||||
position: absolute;
|
||||
height: inherit;
|
||||
outline: initial;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@
|
|||
@import './inventory-fieldset-items.less';
|
||||
@import './prose-mirror.less';
|
||||
@import './filter-menu.less';
|
||||
@import './tab-attachments.less';
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
// Theme handling
|
||||
.appTheme({
|
||||
background: @semi-transparent-dark-blue;
|
||||
backdrop-filter: blur(9px);
|
||||
background: @dark-blue-60;
|
||||
backdrop-filter: blur(10px);
|
||||
}, {
|
||||
background: url('../assets/parchments/dh-parchment-light.png') no-repeat center;
|
||||
});
|
||||
|
|
@ -44,6 +44,8 @@
|
|||
top: -36px;
|
||||
min-height: -webkit-fill-available;
|
||||
transition: opacity 0.3s ease;
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: -36px;
|
||||
|
||||
.tab {
|
||||
padding: 0 10px;
|
||||
|
|
|
|||
7
styles/less/global/tab-attachments.less
Normal file
7
styles/less/global/tab-attachments.less
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
.daggerheart.dh-style {
|
||||
.tab.attachments {
|
||||
.attached-items {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
.tab.experiences {
|
||||
.add-experience-btn {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
.add-feature-btn {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
.add-action-btn {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
.add-feature-btn {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
gap: 15px 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding-bottom: 0;
|
||||
|
||||
.adversary-sidebar-sheet {
|
||||
grid-row: 1 / span 2;
|
||||
|
|
|
|||
|
|
@ -110,10 +110,11 @@
|
|||
justify-content: space-evenly;
|
||||
|
||||
.status-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
justify-items: center;
|
||||
|
||||
.status-label {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
gap: 15px 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding-bottom: 0;
|
||||
overflow: auto;
|
||||
|
||||
.character-sidebar-sheet {
|
||||
|
|
|
|||
|
|
@ -70,10 +70,11 @@
|
|||
justify-content: space-evenly;
|
||||
|
||||
.status-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
justify-items: center;
|
||||
|
||||
.status-label {
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@
|
|||
justify-content: center;
|
||||
|
||||
.status-number {
|
||||
justify-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.status-value {
|
||||
position: relative;
|
||||
|
|
@ -85,6 +87,8 @@
|
|||
}
|
||||
|
||||
.status-bar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
|
|
|
|||
|
|
@ -25,10 +25,12 @@
|
|||
@dark-blue-10: #18162e10;
|
||||
@dark-blue-40: #18162e40;
|
||||
@dark-blue-50: #18162e50;
|
||||
@dark-blue-60: #18162e60;
|
||||
@semi-transparent-dark-blue: rgba(24, 22, 46, 0.33);
|
||||
|
||||
@dark: #222;
|
||||
@dark-15: #22222215;
|
||||
@dark-40: #22222240;
|
||||
|
||||
@deep-black: #0e0d15;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
<form id="action-type-select">
|
||||
<ul class="unlist list-select">
|
||||
<header class="dialog-header">
|
||||
<h1>{{itemName}}</h1>
|
||||
</header>
|
||||
<ul class="actions-list">
|
||||
{{#each types}}
|
||||
<li>
|
||||
<label>
|
||||
{{! TODO: remove dh-icon}}
|
||||
<dh-icon class="dh-icon fas {{icon}}"></dh-icon>
|
||||
<span>{{localize name}}</span>
|
||||
<li class="action-item">
|
||||
<input type="radio" name="type" value="{{id}}" {{#if (eq @index 0)}}checked{{/if}}>
|
||||
</label>
|
||||
<span class="label">{{localize name}}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<form id="item-action-select">
|
||||
<ul class="unlist list-select">
|
||||
<header class="dialog-header">
|
||||
<h1>{{itemName}}</h1>
|
||||
</header>
|
||||
<ul class="actions-list">
|
||||
{{#each actions}}
|
||||
<li>
|
||||
<label>
|
||||
<dh-icon><img src="{{ img }}"></dh-icon>
|
||||
<span>{{ name }}</span>
|
||||
<li class="action-item">
|
||||
<input type="radio" name="actionId" value="{{_id}}" {{#if (eq @index 0)}}checked{{/if}}>
|
||||
</label>
|
||||
<span class="label">{{ name }}</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,33 +1,13 @@
|
|||
<div>
|
||||
<section class="damage-section-container">
|
||||
<header class="dialog-header">
|
||||
<h1>{{title}}</h1>
|
||||
</header>
|
||||
<span class="formula-label"><b>Formula:</b> {{@root.formula}}</span>
|
||||
<div class="form-group">
|
||||
<label><strong>{{title}}</strong></label>
|
||||
<div class="form-fields">
|
||||
{{!-- <input type="text" value="{{formula}}" disabled /> --}}
|
||||
<div>{{@root.formula}}</div>
|
||||
</div>
|
||||
<div>
|
||||
<input type="text" value="{{extraFormula}}" name="extraFormula" placeholder="Situational Bonus">
|
||||
</div>
|
||||
</div>
|
||||
{{!-- {{#each 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 ../hope)}}disabled{{/if}}"></i>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}} --}}
|
||||
<footer>
|
||||
<button data-action="submitRoll">Roll</button>
|
||||
</footer>
|
||||
</div>
|
||||
<button class="submit-btn" data-action="submitRoll">
|
||||
<i class="fa-solid fa-dice"></i>
|
||||
<span class="label">Roll</span>
|
||||
</button>
|
||||
</section>
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
{{formGroup fields.origin value=source.origin rootId=rootId disabled=true}}
|
||||
{{/if}}
|
||||
{{#if isItemEffect}}
|
||||
{{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=legacyTransfer.hint}}
|
||||
{{formGroup fields.transfer value=source.transfer rootId=rootId label=legacyTransfer.label hint=(localize "DAGGERHEART.EFFECTS.Attachments.transferHint")}}
|
||||
{{/if}}
|
||||
|
||||
{{formGroup fields.statuses value=source.statuses options=statuses rootId=rootId classes="statuses"}}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@
|
|||
<div class='status-value'>
|
||||
<p><input class="bar-input" name="system.resources.hitPoints.value" value="{{source.system.resources.hitPoints.value}}" type="number"></p>
|
||||
<p>/</p>
|
||||
<p class="bar-label">{{source.system.resources.hitPoints.max}}</p>
|
||||
<p class="bar-label">{{source.system.resources.hitPoints.maxTotal}}</p>
|
||||
</div>
|
||||
<progress
|
||||
class='progress-bar'
|
||||
value='{{source.system.resources.hitPoints.value}}'
|
||||
max='{{source.system.resources.hitPoints.max}}'
|
||||
max='{{source.system.resources.hitPoints.maxTotal}}'
|
||||
></progress>
|
||||
<div class="status-label">
|
||||
<h4>HP</h4>
|
||||
|
|
@ -26,12 +26,12 @@
|
|||
<div class='status-value'>
|
||||
<p><input class="bar-input" name="system.resources.stress.value" value="{{source.system.resources.stress.value}}" type="number"></p>
|
||||
<p>/</p>
|
||||
<p class="bar-label">{{source.system.resources.stress.max}}</p>
|
||||
<p class="bar-label">{{source.system.resources.stress.maxTotal}}</p>
|
||||
</div>
|
||||
<progress
|
||||
class='progress-bar stress-color'
|
||||
value='{{source.system.resources.stress.value}}'
|
||||
max='{{source.system.resources.stress.max}}'
|
||||
max='{{source.system.resources.stress.maxTotal}}'
|
||||
></progress>
|
||||
<div class="status-label">
|
||||
<h4>Stress</h4>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-type="{{type}}">
|
||||
<li class="inventory-item" data-item-id="{{item.id}}" data-item-uuid="{{item.uuid}}" data-type="{{type}}" draggable="true">
|
||||
<img src="{{item.img}}" class="item-img {{#if isActor}}actor-img{{/if}}" data-action="useItem" {{#if (not noTooltip)}}data-tooltip="{{concat "#item#" item.uuid}}"{{/if}} />
|
||||
<div class="item-label">
|
||||
{{#if isCompanion}}
|
||||
|
|
@ -16,11 +16,11 @@
|
|||
<span> - </span>
|
||||
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
||||
{{!-- ({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' item.system.attack.damage.parts.0.type '.abbreviation')}}) --}}
|
||||
{{#with (lookup @root.config.GENERAL.damageTypes item.system.attack.damage.parts.0.type)}}
|
||||
{{#each icon}}
|
||||
<i class="fa-solid {{this}}"></i>
|
||||
{{/each}}
|
||||
{{#each item.system.attack.damage.parts.0.type as | type | }}
|
||||
{{#with (lookup @root.config.GENERAL.damageTypes type)}}
|
||||
<i class="fa-solid {{icon}}"></i>
|
||||
{{/with}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
|
|
@ -32,7 +32,11 @@
|
|||
</div>
|
||||
<div class="tag">
|
||||
{{item.system.attack.damage.parts.0.value.dice}}{{#if item.system.attack.damage.parts.0.value.bonus}} + {{item.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
||||
({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' item.system.attack.damage.parts.0.type '.abbreviation')}})
|
||||
(
|
||||
{{#each item.system.attack.damage.parts.0.type}}
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}}
|
||||
{{/each}}
|
||||
)
|
||||
</div>
|
||||
<div class="tag">
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Burden.' item.system.burden)}}
|
||||
|
|
|
|||
29
templates/sheets/global/tabs/tab-attachments.hbs
Normal file
29
templates/sheets/global/tabs/tab-attachments.hbs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<section
|
||||
class='tab {{tabs.attachments.cssClass}} {{tabs.attachments.id}}'
|
||||
data-tab='{{tabs.attachments.id}}'
|
||||
data-group='{{tabs.attachments.group}}'
|
||||
>
|
||||
<fieldset class="one-column drop-section attachments-section">
|
||||
<legend>{{localize tabs.attachments.label}}</legend>
|
||||
|
||||
{{#if attachedItems}}
|
||||
<div class="attached-items">
|
||||
{{#each attachedItems as |item|}}
|
||||
<div class="inventory-item attached-item" data-uuid="{{item.uuid}}">
|
||||
<img src="{{item.img}}" alt="{{item.name}}" class="item-img">
|
||||
<div class="item-label">
|
||||
<div class="item-name">{{item.name}}</div>
|
||||
</div>
|
||||
<div class="controls">
|
||||
<a data-action="removeAttachment" data-uuid="{{item.uuid}}"><i class="fa-solid fa-trash remove-attachment"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="drop-area" data-drop-type="Item" style="width: 100%;">
|
||||
<span>{{localize "DAGGERHEART.EFFECTS.Attachments.attachHint"}}</span>
|
||||
</div>
|
||||
</fieldset>
|
||||
</section>
|
||||
0
templates/sheets/items/weapon/attachments.hbs
Normal file
0
templates/sheets/items/weapon/attachments.hbs
Normal file
|
|
@ -15,7 +15,11 @@
|
|||
{{localize (concat 'DAGGERHEART.CONFIG.Range.' source.system.attack.range '.name')}}
|
||||
<span>-</span>
|
||||
{{source.system.attack.damage.parts.0.value.dice}}{{#if source.system.attack.damage.parts.0.value.bonus}} + {{source.system.attack.damage.parts.0.value.bonus}}{{/if}}
|
||||
({{localize (concat 'DAGGERHEART.CONFIG.DamageType.' source.system.attack.damage.parts.0.type '.abbreviation')}})
|
||||
(
|
||||
{{#each source.system.attack.damage.parts.0.type}}
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.DamageType.' this '.abbreviation')}}
|
||||
{{/each}}
|
||||
)
|
||||
<span>-</span>
|
||||
{{localize (concat 'DAGGERHEART.CONFIG.Burden.' source.system.burden)}}
|
||||
</h3>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue