Compare commits

..

No commits in common. "20f42e8a0d0bf45cd998396fb0366dd5941cbf92" and "9453d607078abdad436b64460ac65b5cb0333789" have entirely different histories.

19 changed files with 105 additions and 156 deletions

View file

@ -2532,9 +2532,6 @@
"recovery": { "label": "Recovery" }, "recovery": { "label": "Recovery" },
"type": { "label": "Type" }, "type": { "label": "Type" },
"value": { "label": "Value" } "value": { "label": "Value" }
},
"identifier": {
"label": "Identifier"
} }
}, },
"Ancestry": { "Ancestry": {

View file

@ -22,10 +22,9 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
); );
const orderedArmorSources = getArmorSources(actor).filter(s => !s.disabled); const orderedArmorSources = getArmorSources(actor).filter(s => !s.disabled);
const armor = orderedArmorSources.reduce((acc, { name, document }) => { const armor = orderedArmorSources.reduce((acc, { document }) => {
const { current, max } = document.type === 'armor' ? document.system.armor : document.system.armorData; const { current, max } = document.type === 'armor' ? document.system.armor : document.system.armorData;
acc.push({ acc.push({
name,
effect: document, effect: document,
marks: [...Array(max).keys()].reduce((acc, _, index) => { marks: [...Array(max).keys()].reduce((acc, _, index) => {
const spent = index < current; const spent = index < current;
@ -153,8 +152,14 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
const armorSources = []; const armorSources = [];
for (const source of this.marks.armor) { for (const source of this.marks.armor) {
const parent = source.effect.origin
? await foundry.utils.fromUuid(source.effect.origin)
: source.effect.parent;
const useEffectName = parent.type === 'armor' || parent instanceof Actor;
const label = useEffectName ? source.effect.name : parent.name;
armorSources.push({ armorSources.push({
label: source.name, label: label,
uuid: source.effect.uuid, uuid: source.effect.uuid,
marks: source.marks marks: source.marks
}); });

View file

@ -9,8 +9,7 @@ export default class ClassSheet extends DHBaseItemSheet {
position: { width: 700 }, position: { width: 700 },
actions: { actions: {
removeItemFromCollection: ClassSheet.#removeItemFromCollection, removeItemFromCollection: ClassSheet.#removeItemFromCollection,
removeSuggestedItem: ClassSheet.#removeSuggestedItem, removeSuggestedItem: ClassSheet.#removeSuggestedItem
resetIdentifier: ClassSheet.#resetIdentifier
}, },
tagifyConfigs: [ tagifyConfigs: [
{ {
@ -108,7 +107,6 @@ export default class ClassSheet extends DHBaseItemSheet {
async _prepareContext(_options) { async _prepareContext(_options) {
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.domains = this.document.system.domains; context.domains = this.document.system.domains;
context.subclasses = await this.document.system.getSubclasses();
return context; return context;
} }
@ -131,7 +129,19 @@ export default class ClassSheet extends DHBaseItemSheet {
const itemType = data.type === 'ActiveEffect' ? data.type : item.type; const itemType = data.type === 'ActiveEffect' ? data.type : item.type;
const target = event.target.closest('fieldset.drop-section'); const target = event.target.closest('fieldset.drop-section');
if (['feature', 'ActiveEffect'].includes(itemType)) { if (itemType === 'subclass') {
if (!this.document.system.identifier) {
return ui.notifications.error(
game.i18n.localize('DAGGERHEART.UI.Notifications.classMissingIdentifier')
);
}
if (item.system.classIdentifiers.includes(this.document.system.identifier)) return;
await item.update({
'system.classIdentifiers': [...item.system.classIdentifiers, this.document.system.identifier]
});
} else if (['feature', 'ActiveEffect'].includes(itemType)) {
super._onDrop(event); super._onDrop(event);
} else if (this.document.parent?.type !== 'character') { } else if (this.document.parent?.type !== 'character') {
if (itemType === 'weapon') { if (itemType === 'weapon') {
@ -208,10 +218,4 @@ export default class ClassSheet extends DHBaseItemSheet {
const { target } = element.dataset; const { target } = element.dataset;
await this.document.update({ [`system.characterGuide.${target}`]: null }); await this.document.update({ [`system.characterGuide.${target}`]: null });
} }
static async #resetIdentifier() {
const document = this.document;
const initial = document.system.schema.fields.identifier.getInitialValue(document._source);
document.update({ 'system.identifier': initial });
}
} }

View file

@ -40,27 +40,4 @@ export default class SubclassSheet extends DHBaseItemSheet {
get relatedDocs() { get relatedDocs() {
return this.document.system.features.map(x => x.item); return this.document.system.features.map(x => x.item);
} }
async _onDrop(event) {
event.stopPropagation();
const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
const itemType = data.type === 'ActiveEffect' ? data.type : item.type;
if (itemType === 'class') {
const identifier = item.system.identifier;
if (!identifier) {
return ui.notifications.error(
game.i18n.localize('DAGGERHEART.UI.Notifications.classMissingIdentifier')
);
}
if (this.document.system.classLink.identifier !== identifier) {
const { img, name } = item;
await this.document.update({ 'system.classLink': { identifier, img, name } });
}
return;
}
return super._onDrop(event);
}
} }

View file

@ -2,7 +2,7 @@ import BaseDataItem from './base.mjs';
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import ItemLinkFields from '../fields/itemLinkFields.mjs'; import ItemLinkFields from '../fields/itemLinkFields.mjs';
import { addLinkedItemsDiff, camelize, getFeaturesHTMLData, updateLinkedItemApps } from '../../helpers/utils.mjs'; import { addLinkedItemsDiff, getFeaturesHTMLData, updateLinkedItemApps } from '../../helpers/utils.mjs';
export default class DHClass extends BaseDataItem { export default class DHClass extends BaseDataItem {
/** @inheritDoc */ /** @inheritDoc */
@ -51,7 +51,7 @@ export default class DHClass extends BaseDataItem {
backgroundQuestions: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }), backgroundQuestions: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }),
connections: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }), connections: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }),
isMulticlass: new fields.BooleanField({ initial: false }), isMulticlass: new fields.BooleanField({ initial: false }),
identifier: new fields.StringField({ blank: false, initial: obj => camelize(obj?.name ?? '') }), identifier: new fields.StringField(),
/* Subclasses is legacy. If we can safetely migrate it away at some point we could remove it*/ /* Subclasses is legacy. If we can safetely migrate it away at some point we could remove it*/
subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false }) subclasses: new ForeignDocumentUUIDArrayField({ type: 'Item', required: false })
}; };
@ -73,23 +73,26 @@ export default class DHClass extends BaseDataItem {
} }
async getSubclasses() { async getSubclasses() {
const oldLinkedSubclasses = this.subclasses; const oldLinkedSubclasses = this.subclasses.filter(x => x);
if (oldLinkedSubclasses.length) return oldLinkedSubclasses; if (!this.identifier) return oldLinkedSubclasses;
const subclasses = game.items.filter( const subclasses = game.items.filter(
x => x.type === 'subclass' && x.system.classLink.identifier === this.identifier x => x.type === 'subclass' && x.system.classIdentifiers.includes(this.identifier)
); );
for (const pack of game.packs) { for (const pack of game.packs) {
const indexes = await pack.getIndex({ fields: ['system.classLink.identifier'] }); const indexes = await pack.getIndex({ fields: ['system.classIdentifiers'] });
for (const index of indexes) { for (const index of indexes) {
if (index.type !== 'subclass') continue; if (
if (index.system?.classLink?.identifier !== this.identifier) continue; index.type === 'subclass' &&
if (subclasses.find(x => x.uuid === index.uuid)) continue; (index.system.classIdentifiers ?? []).includes(
this.identifier && !subclasses.find(x => x.uuid === index.uuid)
)
) {
const subclass = await foundry.utils.fromUuid(index.uuid); const subclass = await foundry.utils.fromUuid(index.uuid);
subclasses.push(subclass); subclasses.push(subclass);
} }
} }
}
return subclasses; return subclasses;
} }

View file

@ -28,11 +28,7 @@ export default class DHSubclass extends BaseDataItem {
features: new ItemLinkFields(), features: new ItemLinkFields(),
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }), featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
isMulticlass: new fields.BooleanField({ initial: false }), isMulticlass: new fields.BooleanField({ initial: false }),
classLink: new fields.SchemaField({ classIdentifiers: new fields.ArrayField(new fields.StringField({ nullable: false })),
identifier: new fields.StringField({ nullable: true, initial: null }),
name: new fields.StringField(),
img: new fields.StringField()
}),
/* Linked class is legacy. If we can safetely migrate it away at some point we could remove it */ /* Linked class is legacy. If we can safetely migrate it away at some point we could remove it */
linkedClass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true, initial: null }) linkedClass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true, initial: null })
}; };

View file

@ -171,7 +171,6 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
/** Recursively finds the first parent document of the given object */ /** Recursively finds the first parent document of the given object */
static #resolveParentDocument(model, documentClass) { static #resolveParentDocument(model, documentClass) {
if (!model) return null;
return model instanceof documentClass return model instanceof documentClass
? model ? model
: model.parent : model.parent

View file

@ -757,12 +757,9 @@ export function getArmorSources(actor) {
// Get the origin item. Since the actor is already loaded, it should already be cached // Get the origin item. Since the actor is already loaded, it should already be cached
// Consider the relative function versions if this causes an issue // Consider the relative function versions if this causes an issue
const origin = doc.origin ? foundry.utils.fromUuidSync(doc.origin) : doc; const origin = doc.origin ? foundry.utils.fromUuidSync(doc.origin) : doc;
const useParentName = doc.parent && !(doc.parent instanceof Actor);
const name = doc.origin || !useParentName ? doc.name : doc.parent.name;
return { return {
origin, origin,
name, name: origin.name,
document: doc, document: doc,
data: doc.system.armor ?? doc.system.armorData, data: doc.system.armor ?? doc.system.armorData,
disabled: !!doc.disabled || !!doc.isSuppressed disabled: !!doc.disabled || !!doc.isSuppressed
@ -841,11 +838,3 @@ export function createShallowProxy(obj) {
} }
}); });
} }
export function camelize(str) {
return str
.replace(/(?:^\w|[A-Z]|\b\w)/g, (part, index) => {
return index === 0 ? part.toLowerCase() : part.toUpperCase();
})
.replace(/\s+/g, '');
}

View file

@ -40,8 +40,7 @@
"experiences": { "experiences": {
"ti3Z1mq2M92KK4GJ": { "ti3Z1mq2M92KK4GJ": {
"name": "Bloodthirsty", "name": "Bloodthirsty",
"description": "", "description": ""
"value": 3
} }
}, },
"bonuses": { "bonuses": {
@ -243,24 +242,27 @@
"type": "withinRange", "type": "withinRange",
"target": "hostile", "target": "hostile",
"range": "melee" "range": "melee"
},
"changes": [
{
"key": "system.difficulty",
"value": 3,
"priority": null,
"type": "add"
} }
]
}, },
"_id": "qZfNiqw1iAIxeuYg", "_id": "qZfNiqw1iAIxeuYg",
"img": "icons/commodities/biological/wing-lizard-brown.webp", "img": "icons/commodities/biological/wing-lizard-brown.webp",
"changes": [
{
"key": "system.difficulty",
"mode": 2,
"value": "3",
"priority": null
}
],
"disabled": false, "disabled": false,
"duration": { "duration": {
"value": null, "startTime": null,
"units": "seconds", "combat": null,
"expiry": null, "seconds": null,
"expired": false "rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
}, },
"description": "<p>While flying, the Bat gains a +3 bonus to their Difficulty.</p>", "description": "<p>While flying, the Bat gains a +3 bonus to their Difficulty.</p>",
"origin": null, "origin": null,
@ -272,9 +274,6 @@
"_stats": { "_stats": {
"compendiumSource": null "compendiumSource": null
}, },
"start": null,
"showIcon": 1,
"folder": null,
"_key": "!actors.items.effects!tBWHW00epmMnkawe.gx22MpD8fWoi8klZ.qZfNiqw1iAIxeuYg" "_key": "!actors.items.effects!tBWHW00epmMnkawe.gx22MpD8fWoi8klZ.qZfNiqw1iAIxeuYg"
} }
], ],

View file

@ -138,9 +138,12 @@
"src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg", "src": "systems/daggerheart/assets/icons/documents/actors/dragon-head.svg",
"anchorX": 0.5, "anchorX": 0.5,
"anchorY": 0.5, "anchorY": 0.5,
"offsetX": 0,
"offsetY": 0,
"fit": "contain", "fit": "contain",
"scaleX": 1, "scaleX": 1,
"scaleY": 1, "scaleY": 1,
"rotation": 0,
"tint": "#ffffff", "tint": "#ffffff",
"alphaThreshold": 0.75 "alphaThreshold": 0.75
}, },
@ -191,7 +194,7 @@
"saturation": 0, "saturation": 0,
"contrast": 0 "contrast": 0
}, },
"detectionModes": {}, "detectionModes": [],
"occludable": { "occludable": {
"radius": 0 "radius": 0
}, },
@ -217,8 +220,7 @@
"flags": {}, "flags": {},
"randomImg": false, "randomImg": false,
"appendNumber": false, "appendNumber": false,
"prependAdjective": false, "prependAdjective": false
"depth": 1
}, },
"items": [ "items": [
{ {

View file

@ -19,7 +19,6 @@
&:last-child { &:last-child {
margin-bottom: 0px; margin-bottom: 0px;
} }
}
.feature-line { .feature-line {
display: grid; display: grid;
align-items: center; align-items: center;
@ -47,3 +46,4 @@
} }
} }
} }
}

View file

@ -10,8 +10,4 @@
font-family: @font-body; font-family: @font-body;
color: light-dark(@chat-blue-bg, @beige-50); color: light-dark(@chat-blue-bg, @beige-50);
} }
button.plain.inline-control {
flex: 0 0 auto;
}
} }

View file

@ -2,7 +2,7 @@
"id": "daggerheart", "id": "daggerheart",
"title": "Daggerheart", "title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system", "description": "An unofficial implementation of the Daggerheart system",
"version": "2.2.2", "version": "2.2.1",
"compatibility": { "compatibility": {
"minimum": "14.359", "minimum": "14.359",
"verified": "14.360", "verified": "14.360",
@ -10,7 +10,7 @@
}, },
"url": "https://github.com/Foundryborne/daggerheart", "url": "https://github.com/Foundryborne/daggerheart",
"manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json", "manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json",
"download": "https://github.com/Foundryborne/daggerheart/releases/download/2.2.2/system.zip", "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.2.1/system.zip",
"authors": [ "authors": [
{ {
"name": "WBHarry" "name": "WBHarry"

View file

@ -27,7 +27,10 @@
<fieldset> <fieldset>
<legend>{{localize "TYPES.Item.subclass"}}</legend> <legend>{{localize "TYPES.Item.subclass"}}</legend>
<div class="feature-list"> <div class="feature-list">
{{#each subclasses as |subclass index|}} {{#unless source.system.subclasses}}
<div class="drag-area">{{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "DAGGERHEART.GENERAL.subclasses")}}</div>
{{/unless}}
{{#each source.system.subclasses as |subclass index|}}
<li class='feature-item'> <li class='feature-item'>
<div class='feature-line'> <div class='feature-line'>
<img class='image' src='{{subclass.img}}' /> <img class='image' src='{{subclass.img}}' />
@ -43,7 +46,6 @@
> >
<i class="fa-solid fa-globe"></i> <i class="fa-solid fa-globe"></i>
</a> </a>
{{#if document.system.subclasses}}
<a <a
class='effect-control' class='effect-control'
data-action='removeItemFromCollection' data-action='removeItemFromCollection'
@ -53,7 +55,6 @@
> >
<i class='fas fa-trash'></i> <i class='fas fa-trash'></i>
</a> </a>
{{/if}}
</div> </div>
</div> </div>
</li> </li>

View file

@ -3,6 +3,10 @@
<div class='item-info'> <div class='item-info'>
<h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1> <h1 class='item-name'><input type='text' name='name' value='{{source.name}}' /></h1>
<div class='item-description'> <div class='item-description'>
<h3 class="flexrow">
{{localize 'TYPES.Item.class'}}
{{formInput systemFields.identifier value=source.system.identifier}}
</h3>
<h3 class="form-fields domain-section"> <h3 class="form-fields domain-section">
<span>{{localize "DAGGERHEART.GENERAL.Domain.plural"}}</span> <span>{{localize "DAGGERHEART.GENERAL.Domain.plural"}}</span>
<input class="domain-input" value="{{domains}}" /> <input class="domain-input" value="{{domains}}" />

View file

@ -3,15 +3,6 @@
data-tab='{{tabs.settings.id}}' data-tab='{{tabs.settings.id}}'
data-group='{{tabs.settings.group}}' data-group='{{tabs.settings.group}}'
> >
<fieldset class="two-columns">
<legend>{{localize "DAGGERHEART.GENERAL.general"}}</legend>
<span>{{localize "DAGGERHEART.ITEMS.FIELDS.identifier.label"}}</span>
<div class="flexrow">
{{formInput systemFields.identifier value=source.system.identifier}}
<button class="plain inline-control icon fa-solid fa-rotate-right" data-action="resetIdentifier"></button>
</div>
</fieldset>
<fieldset class="two-columns even"> <fieldset class="two-columns even">
<legend>{{localize tabs.settings.label}}</legend> <legend>{{localize tabs.settings.label}}</legend>
{{formGroup systemFields.hitPoints value=source.system.hitPoints localize=true}} {{formGroup systemFields.hitPoints value=source.system.hitPoints localize=true}}

View file

@ -3,20 +3,6 @@
data-tab='{{tabs.features.id}}' data-tab='{{tabs.features.id}}'
data-group='{{tabs.features.group}}' data-group='{{tabs.features.group}}'
> >
<fieldset>
<legend>{{localize "TYPES.Item.class"}}</legend>
{{#if document.system.classLink.identifier}}
<div class="feature-list">
<li class="feature-line">
<img class="image" src="{{document.system.classLink.img}}" />
<span>{{document.system.classLink.name}}</span>
</li>
</div>
{{else}}
<div class="drag-area">{{localize "DAGGERHEART.GENERAL.missingDragDropThing" thing=(localize "TYPES.Item.class")}}</div>
{{/if}}
</fieldset>
<fieldset class="drop-section" data-type="foundation"> <fieldset class="drop-section" data-type="foundation">
<legend> <legend>
{{localize "DAGGERHEART.GENERAL.Tabs.foundation"}} {{localize "DAGGERHEART.GENERAL.Tabs.foundation"}}