mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
Iss4 - create a way to attach attach items to armor and weapons (#310)
* add basic drag drop window * add better field * make effects copy onto actor on attachment * make items from inventory draggable * working drop from inventory * remove duplication issue * add attachment only flag and logic * add weapons to attachables * remove debug logs * try to make it drier * remove unecessary try catch * remove extra configs * remove superfluous comments * remove spurious defenses * make drier * remove unecessary code * deduplicate and simplify * its a desert * standardize to be more similar to class item code * fix bug of duplicate effects being created * fix localization string * fix bug of item equiping and un equiping * remove this since were not going to be using attachmentonly * update attachment tab with comments * remove attachment only logic in favor of just transfer * change flags * change armor and weapon to be attachableItem * change armor and weapon to be attachableItem * change weapon to use mixin * add mixin to armor * move everything to mixin sheet * refactor code for review comments * cleanup and somehow git is ignoring some changes * see if this picks up the changes now * Import/Export updates --------- Co-authored-by: psitacus <walther.johnson@ucalgary.ca> Co-authored-by: WBHarry <williambjrklund@gmail.com>
This commit is contained in:
parent
812a5e8dd7
commit
687500f191
18 changed files with 357 additions and 18 deletions
|
|
@ -389,6 +389,7 @@
|
||||||
"default": "Default Ownership"
|
"default": "Default Ownership"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"CONFIG": {
|
"CONFIG": {
|
||||||
"ActionType": {
|
"ActionType": {
|
||||||
"passive": "Passive",
|
"passive": "Passive",
|
||||||
|
|
@ -958,6 +959,10 @@
|
||||||
"stress": {
|
"stress": {
|
||||||
"name": "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": {
|
"GENERAL": {
|
||||||
|
|
@ -1093,7 +1098,8 @@
|
||||||
"optional": "Optional",
|
"optional": "Optional",
|
||||||
"recovery": "Recovery",
|
"recovery": "Recovery",
|
||||||
"setup": "Setup",
|
"setup": "Setup",
|
||||||
"equipment": "Equipment"
|
"equipment": "Equipment",
|
||||||
|
"attachments": "Attachments"
|
||||||
},
|
},
|
||||||
"Tiers": {
|
"Tiers": {
|
||||||
"singular": "Tier",
|
"singular": "Tier",
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,12 @@ export default class CharacterSheet extends DHBaseActorSheet {
|
||||||
window: {
|
window: {
|
||||||
resizable: true
|
resizable: true
|
||||||
},
|
},
|
||||||
dragDrop: [],
|
dragDrop: [
|
||||||
|
{
|
||||||
|
dragSelector: '[data-item-id][draggable="true"]',
|
||||||
|
dropSelector: null
|
||||||
|
}
|
||||||
|
],
|
||||||
contextMenus: [
|
contextMenus: [
|
||||||
{
|
{
|
||||||
handler: CharacterSheet._getContextMenuOptions,
|
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);
|
super._onDragStart(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onDrop(event) {
|
async _onDrop(event) {
|
||||||
|
// Prevent event bubbling to avoid duplicate handling
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
super._onDrop(event);
|
super._onDrop(event);
|
||||||
this._onDropItem(event, TextEditor.getDragEventData(event));
|
this._onDropItem(event, TextEditor.getDragEventData(event));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export { default as DHApplicationMixin } from './application-mixin.mjs';
|
export { default as DHApplicationMixin } from './application-mixin.mjs';
|
||||||
export { default as DHBaseItemSheet } from './base-item.mjs';
|
export { default as DHBaseItemSheet } from './base-item.mjs';
|
||||||
export { default as DHHeritageSheet } from './heritage-sheet.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 DHBaseActorSheet } from './base-actor.mjs';
|
||||||
export { default as DHBaseActorSettings } from './actor-setting.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 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 */
|
/**@inheritdoc */
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
classes: ['armor'],
|
classes: ['armor'],
|
||||||
dragDrop: [{ dragSelector: null, dropSelector: null }],
|
|
||||||
tagifyConfigs: [
|
tagifyConfigs: [
|
||||||
{
|
{
|
||||||
selector: '.features-input',
|
selector: '.features-input',
|
||||||
|
|
@ -26,7 +26,8 @@ export default class ArmorSheet extends DHBaseItemSheet {
|
||||||
settings: {
|
settings: {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/armor/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
}
|
},
|
||||||
|
...super.PARTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import DHBaseItemSheet from '../api/base-item.mjs';
|
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 */
|
/**@inheritdoc */
|
||||||
static DEFAULT_OPTIONS = {
|
static DEFAULT_OPTIONS = {
|
||||||
classes: ['weapon'],
|
classes: ['weapon'],
|
||||||
|
|
@ -25,12 +26,13 @@ export default class WeaponSheet extends DHBaseItemSheet {
|
||||||
settings: {
|
settings: {
|
||||||
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
template: 'systems/daggerheart/templates/sheets/items/weapon/settings.hbs',
|
||||||
scrollable: ['.settings']
|
scrollable: ['.settings']
|
||||||
}
|
},
|
||||||
|
...super.PARTS,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**@inheritdoc */
|
/**@inheritdoc */
|
||||||
async _preparePartContext(partId, context) {
|
async _preparePartContext(partId, context) {
|
||||||
super._preparePartContext(partId, context);
|
await super._preparePartContext(partId, context);
|
||||||
switch (partId) {
|
switch (partId) {
|
||||||
case 'settings':
|
case 'settings':
|
||||||
context.features = this.document.system.weaponFeatures.map(x => x.value);
|
context.features = this.document.system.weaponFeatures.map(x => x.value);
|
||||||
|
|
|
||||||
|
|
@ -7,3 +7,5 @@ export const encounterCountdown = {
|
||||||
simple: 'countdown-encounter-simple',
|
simple: 'countdown-encounter-simple',
|
||||||
position: 'countdown-encounter-position'
|
position: 'countdown-encounter-position'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const itemAttachmentSource = 'attachmentSource';
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import DHAncestry from './ancestry.mjs';
|
import DHAncestry from './ancestry.mjs';
|
||||||
import DHArmor from './armor.mjs';
|
import DHArmor from './armor.mjs';
|
||||||
|
import DHAttachableItem from './attachableItem.mjs';
|
||||||
import DHClass from './class.mjs';
|
import DHClass from './class.mjs';
|
||||||
import DHCommunity from './community.mjs';
|
import DHCommunity from './community.mjs';
|
||||||
import DHConsumable from './consumable.mjs';
|
import DHConsumable from './consumable.mjs';
|
||||||
|
|
@ -13,6 +14,7 @@ import DHBeastform from './beastform.mjs';
|
||||||
export {
|
export {
|
||||||
DHAncestry,
|
DHAncestry,
|
||||||
DHArmor,
|
DHArmor,
|
||||||
|
DHAttachableItem,
|
||||||
DHClass,
|
DHClass,
|
||||||
DHCommunity,
|
DHCommunity,
|
||||||
DHConsumable,
|
DHConsumable,
|
||||||
|
|
@ -27,6 +29,7 @@ export {
|
||||||
export const config = {
|
export const config = {
|
||||||
ancestry: DHAncestry,
|
ancestry: DHAncestry,
|
||||||
armor: DHArmor,
|
armor: DHArmor,
|
||||||
|
attachableItem: DHAttachableItem,
|
||||||
class: DHClass,
|
class: DHClass,
|
||||||
community: DHCommunity,
|
community: DHCommunity,
|
||||||
consumable: DHConsumable,
|
consumable: DHConsumable,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import BaseDataItem from './base.mjs';
|
import AttachableItem from './attachableItem.mjs';
|
||||||
import ActionField from '../fields/actionField.mjs';
|
import ActionField from '../fields/actionField.mjs';
|
||||||
import { armorFeatures } from '../../config/itemConfig.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 */
|
/** @inheritDoc */
|
||||||
static get metadata() {
|
static get metadata() {
|
||||||
return foundry.utils.mergeObject(super.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 { actionsTypes } from '../action/_module.mjs';
|
||||||
import ActionField from '../fields/actionField.mjs';
|
import ActionField from '../fields/actionField.mjs';
|
||||||
|
|
||||||
export default class DHWeapon extends BaseDataItem {
|
export default class DHWeapon extends AttachableItem {
|
||||||
/** @inheritDoc */
|
/** @inheritDoc */
|
||||||
static get metadata() {
|
static get metadata() {
|
||||||
return foundry.utils.mergeObject(super.metadata, {
|
return foundry.utils.mergeObject(super.metadata, {
|
||||||
|
|
@ -37,7 +37,7 @@ export default class DHWeapon extends BaseDataItem {
|
||||||
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
attack: new ActionField({
|
attack: new ActionField({
|
||||||
initial: {
|
initial: {
|
||||||
name: 'Attack',
|
name: 'Attack',
|
||||||
img: 'icons/skills/melee/blood-slash-foam-red.webp',
|
img: 'icons/skills/melee/blood-slash-foam-red.webp',
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,42 @@
|
||||||
export default class DhActiveEffect extends ActiveEffect {
|
export default class DhActiveEffect extends ActiveEffect {
|
||||||
get isSuppressed() {
|
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;
|
return !this.parent.system.equipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.parent.type === 'domainCard') {
|
if (this.parent?.type === 'domainCard') {
|
||||||
return this.parent.system.inVault;
|
return this.parent.system.inVault;
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.isSuppressed;
|
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) {
|
async _preCreate(data, options, user) {
|
||||||
const update = {};
|
const update = {};
|
||||||
if (!data.img) {
|
if (!data.img) {
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,4 @@
|
||||||
@import './inventory-fieldset-items.less';
|
@import './inventory-fieldset-items.less';
|
||||||
@import './prose-mirror.less';
|
@import './prose-mirror.less';
|
||||||
@import './filter-menu.less';
|
@import './filter-menu.less';
|
||||||
|
@import './tab-attachments.less';
|
||||||
|
|
|
||||||
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%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
{{formGroup fields.origin value=source.origin rootId=rootId disabled=true}}
|
{{formGroup fields.origin value=source.origin rootId=rootId disabled=true}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if isItemEffect}}
|
{{#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}}
|
{{/if}}
|
||||||
|
|
||||||
{{formGroup fields.statuses value=source.statuses options=statuses rootId=rootId classes="statuses"}}
|
{{formGroup fields.statuses value=source.statuses options=statuses rootId=rootId classes="statuses"}}
|
||||||
|
|
|
||||||
|
|
@ -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}} />
|
<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">
|
<div class="item-label">
|
||||||
{{#if isCompanion}}
|
{{#if isCompanion}}
|
||||||
|
|
|
||||||
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
Loading…
Add table
Add a link
Reference in a new issue