mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-17 07:36:26 +01:00
Fixed hybrid
This commit is contained in:
parent
011f5d2b14
commit
796cec01ee
13 changed files with 464 additions and 110 deletions
23
lang/en.json
23
lang/en.json
|
|
@ -106,6 +106,10 @@
|
|||
"horderHp": "Horde/HP"
|
||||
},
|
||||
"Character": {
|
||||
"advantageSources": {
|
||||
"label": "Advantage Sources",
|
||||
"hint": "Add single words or short text as reminders and hints of what a character has advantage on."
|
||||
},
|
||||
"age": "Age",
|
||||
"companionFeatures": "Companion Features",
|
||||
"contextMenu": {
|
||||
|
|
@ -117,6 +121,10 @@
|
|||
"unequip": "Unequip",
|
||||
"useItem": "Use Item"
|
||||
},
|
||||
"disadvantageSources": {
|
||||
"label": "Disadvantage Sources",
|
||||
"hint": "Add single words or short text as reminders and hints of what a character has disadvantage on."
|
||||
},
|
||||
"faith": "Faith",
|
||||
"levelUp": "You can level up",
|
||||
"pronouns": "Pronouns",
|
||||
|
|
@ -1014,6 +1022,7 @@
|
|||
},
|
||||
"Advantage": {
|
||||
"full": "Advantage",
|
||||
"plural": "Advantages",
|
||||
"short": "Adv"
|
||||
},
|
||||
"Adversary": {
|
||||
|
|
@ -1169,6 +1178,11 @@
|
|||
"hint": "The cost in stress you can pay to reduce minor damage to none."
|
||||
}
|
||||
}
|
||||
},
|
||||
"attack": {
|
||||
"damage": {
|
||||
"value": { "label": "Base Attack: Damage" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tabs": {
|
||||
|
|
@ -1266,6 +1280,7 @@
|
|||
"title": "Title",
|
||||
"true": "True",
|
||||
"type": "Type",
|
||||
"unarmedStrike": "Unarmed Strike",
|
||||
"unarmored": "Unarmored",
|
||||
"use": "Use",
|
||||
"used": "Used",
|
||||
|
|
@ -1327,8 +1342,10 @@
|
|||
"beastformEffect": "Beastform Transformation",
|
||||
"evolve": "Evolve",
|
||||
"evolvedFeatureTitle": "Evolved",
|
||||
"evolvedDrag": "Drag a form here to evolve it.",
|
||||
"hybridize": "Hybridize",
|
||||
"hybridFeatureTitle": "Hybrid Features"
|
||||
"hybridizeFeatureTitle": "Hybrid Features",
|
||||
"hybridizeDrag": "Drag a form here to hybridize it."
|
||||
},
|
||||
"Class": {
|
||||
"hopeFeatures": "Hope Features",
|
||||
|
|
@ -1573,7 +1590,9 @@
|
|||
"featureNotFoundation": "This feature is used as something else than a Foundation feature and cannot be used here.",
|
||||
"featureNotSpecialization": "This feature is used as something else than a Specialization feature and cannot be used here.",
|
||||
"featureNotMastery": "This feature is used as something else than a Mastery feature and cannot be used here.",
|
||||
"beastformMissingEffect": "The Beastform is missing a Beastform Effect. Cannot be used."
|
||||
"beastformMissingEffect": "The Beastform is missing a Beastform Effect. Cannot be used.",
|
||||
"beastformToManyAdvantages": "You cannot select any more advantages.",
|
||||
"beastformToManyFeatures": "You cannot select any more features."
|
||||
},
|
||||
"Tooltip": {
|
||||
"openItemWorld": "Open Item World",
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
this.configData = configData;
|
||||
this.selected = null;
|
||||
this.evolved = { form: null };
|
||||
this.hybrid = null;
|
||||
this.hybrid = { forms: {}, advantages: {}, features: {} };
|
||||
|
||||
this._dragDrop = this._createDragDropHandlers();
|
||||
}
|
||||
|
|
@ -21,6 +21,8 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
},
|
||||
actions: {
|
||||
selectBeastform: this.selectBeastform,
|
||||
toggleHybridFeature: this.toggleHybridFeature,
|
||||
toggleHybridAdvantage: this.toggleHybridAdvantage,
|
||||
submitBeastform: this.submitBeastform
|
||||
},
|
||||
form: {
|
||||
|
|
@ -38,7 +40,7 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
/** @override */
|
||||
static PARTS = {
|
||||
beastform: {
|
||||
template: 'systems/daggerheart/templates/dialogs/beastformDialog.hbs'
|
||||
template: 'systems/daggerheart/templates/dialogs/beastform/beastformDialog.hbs'
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -65,7 +67,33 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
context.selectedBeastformEffect = this.selected?.effects?.find?.(x => x.type === 'beastform');
|
||||
|
||||
context.evolved = this.evolved;
|
||||
context.hybrid = this.hybrid;
|
||||
|
||||
context.hybridForms = Object.keys(this.hybrid.forms).reduce((acc, formKey) => {
|
||||
if (!this.hybrid.forms[formKey]) {
|
||||
acc[formKey] = null;
|
||||
} else {
|
||||
const data = this.hybrid.forms[formKey].toObject();
|
||||
acc[formKey] = {
|
||||
...data,
|
||||
system: {
|
||||
...data.system,
|
||||
features: this.hybrid.forms[formKey].system.features.map(feature => ({
|
||||
...feature.toObject(),
|
||||
uuid: feature.uuid,
|
||||
selected: Boolean(this.hybrid.features?.[formKey]?.[feature.uuid])
|
||||
})),
|
||||
advantageOn: Object.keys(data.system.advantageOn).reduce((acc, key) => {
|
||||
acc[key] = {
|
||||
...data.system.advantageOn[key],
|
||||
selected: Boolean(this.hybrid.advantages?.[formKey]?.[key])
|
||||
};
|
||||
return acc;
|
||||
}, {})
|
||||
}
|
||||
};
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const maximumDragTier = Math.max(
|
||||
this.selected?.system?.evolved?.maximumTier ?? 0,
|
||||
|
|
@ -83,13 +111,16 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
acc[tier.id].values[x.uuid] = {
|
||||
selected: this.selected?.uuid == x.uuid,
|
||||
value: x,
|
||||
draggable: maximumDragTier ? x.system.tier <= maximumDragTier : false
|
||||
draggable:
|
||||
!['evolved', 'hybrid'].includes(x.system.beastformType) && maximumDragTier
|
||||
? x.system.tier <= maximumDragTier
|
||||
: false
|
||||
};
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
); // Also get from compendium when added
|
||||
);
|
||||
|
||||
context.canSubmit = this.canSubmit();
|
||||
|
||||
|
|
@ -103,6 +134,19 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
return true;
|
||||
case 'evolved':
|
||||
return this.evolved.form;
|
||||
case 'hybrid':
|
||||
const selectedAdvantages = Object.values(this.hybrid.advantages).reduce(
|
||||
(acc, form) => acc + Object.values(form).length,
|
||||
0
|
||||
);
|
||||
const selectedFeatures = Object.values(this.hybrid.features).reduce(
|
||||
(acc, form) => acc + Object.values(form).length,
|
||||
0
|
||||
);
|
||||
|
||||
const advantagesSelected = selectedAdvantages === this.selected.system.hybrid.advantages;
|
||||
const featuresSelected = selectedFeatures === this.selected.system.hybrid.features;
|
||||
return advantagesSelected && featuresSelected;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +179,62 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
|
||||
if (this.selected) {
|
||||
if (this.selected.system.beastformType !== 'evolved') this.evolved.form = null;
|
||||
if (this.selected.system.beastformType !== 'hybrid') this.hybrid = null;
|
||||
if (this.selected.system.beastformType !== 'hybrid') {
|
||||
this.hybrid.forms = {};
|
||||
this.hybrid.advantages = {};
|
||||
this.hybrid.features = {};
|
||||
} else {
|
||||
this.hybrid.forms = [...Array(this.selected.system.hybrid.beastformOptions).keys()].reduce((acc, _) => {
|
||||
acc[foundry.utils.randomID()] = null;
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static toggleHybridFeature(_, button) {
|
||||
const current = this.hybrid.features[button.dataset.form];
|
||||
if (!current) this.hybrid.features[button.dataset.form] = {};
|
||||
|
||||
if (this.hybrid.features[button.dataset.form][button.id])
|
||||
delete this.hybrid.features[button.dataset.form][button.id];
|
||||
else {
|
||||
const currentFeatures = Object.values(this.hybrid.features).reduce(
|
||||
(acc, form) => acc + Object.values(form).length,
|
||||
0
|
||||
);
|
||||
if (currentFeatures === this.selected.system.hybrid.features) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformToManyFeatures'));
|
||||
return;
|
||||
}
|
||||
|
||||
const feature = this.hybrid.forms[button.dataset.form].system.features.find(x => x.uuid === button.id);
|
||||
this.hybrid.features[button.dataset.form][button.id] = feature;
|
||||
}
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
||||
static toggleHybridAdvantage(_, button) {
|
||||
const current = this.hybrid.advantages[button.dataset.form];
|
||||
if (!current) this.hybrid.advantages[button.dataset.form] = {};
|
||||
|
||||
if (this.hybrid.advantages[button.dataset.form][button.id])
|
||||
delete this.hybrid.advantages[button.dataset.form][button.id];
|
||||
else {
|
||||
const currentAdvantages = Object.values(this.hybrid.advantages).reduce(
|
||||
(acc, form) => acc + Object.values(form).length,
|
||||
0
|
||||
);
|
||||
if (currentAdvantages === this.selected.system.hybrid.advantages) {
|
||||
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.beastformToManyAdvantages'));
|
||||
return;
|
||||
}
|
||||
|
||||
const advantage = this.hybrid.forms[button.dataset.form].system.advantageOn[button.id];
|
||||
this.hybrid.advantages[button.dataset.form][button.id] = advantage;
|
||||
}
|
||||
|
||||
this.render();
|
||||
|
|
@ -164,23 +263,17 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
|
||||
async _onDragStart(event) {
|
||||
const target = event.currentTarget;
|
||||
if (!this.selected) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
const abort = () => event.preventDefault();
|
||||
if (!this.selected) abort();
|
||||
|
||||
const draggedForm = await foundry.utils.fromUuid(target.dataset.uuid);
|
||||
if (['evolved', 'hybrid'].includes(draggedForm.system.beastformType)) abort();
|
||||
|
||||
if (this.selected.system.beastformType === 'evolved') {
|
||||
if (draggedForm.system.tier > this.selected.system.evolved.maximumTier) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (draggedForm.system.tier > this.selected.system.evolved.maximumTier) abort();
|
||||
}
|
||||
if (this.selected.system.beastformType === 'hybrid') {
|
||||
if (draggedForm.system.tier > this.selected.system.hybrid.maximumTier) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (draggedForm.system.tier > this.selected.system.hybrid.maximumTier) abort();
|
||||
}
|
||||
|
||||
event.dataTransfer.setData('text/plain', JSON.stringify(target.dataset));
|
||||
|
|
@ -191,9 +284,20 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
|
|||
event.stopPropagation();
|
||||
const data = foundry.applications.ux.TextEditor.getDragEventData(event);
|
||||
const item = await fromUuid(data.uuid);
|
||||
if (!item) return;
|
||||
|
||||
if (event.target.closest('.advanced-form-container.evolved')) {
|
||||
this.evolved.form = item;
|
||||
} else {
|
||||
const hybridContainer = event.target.closest('.advanced-form-container.hybridized');
|
||||
if (hybridContainer) {
|
||||
const existingId = Object.keys(this.hybrid.forms).find(
|
||||
key => this.hybrid.forms[key]?.uuid === item.uuid
|
||||
);
|
||||
if (existingId) this.hybrid.forms[existingId] = null;
|
||||
|
||||
this.hybrid.forms[hybridContainer.id] = item;
|
||||
}
|
||||
}
|
||||
|
||||
this.render();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,25 @@ export default class BeastformSheet extends DHBaseItemSheet {
|
|||
|
||||
const advantageOnInput = htmlElement.querySelector('.advantageon-input');
|
||||
if (advantageOnInput) {
|
||||
const tagifyElement = new Tagify(advantageOnInput);
|
||||
const tagifyElement = new Tagify(advantageOnInput, {
|
||||
tagTextProp: 'name',
|
||||
templates: {
|
||||
tag(tagData) {
|
||||
return `<tag
|
||||
contenteditable='false'
|
||||
spellcheck='false'
|
||||
tabIndex="${this.settings.a11y.focusableTags ? 0 : -1}"
|
||||
class="${this.settings.classNames.tag} ${tagData.class ? tagData.class : ''}"
|
||||
${this.getAttributes(tagData)}>
|
||||
<x class="${this.settings.classNames.tagX}" role='button' aria-label='remove tag'></x>
|
||||
<div>
|
||||
<span class="${this.settings.classNames.tagText}">${tagData[this.settings.tagTextProp] || tagData.value}</span>
|
||||
${tagData.src ? `<img src="${tagData.src}"></i>` : ''}
|
||||
</div>
|
||||
</tag>`;
|
||||
}
|
||||
}
|
||||
});
|
||||
tagifyElement.on('add', this.advantageOnAdd.bind(this));
|
||||
tagifyElement.on('remove', this.advantageOnRemove.bind(this));
|
||||
}
|
||||
|
|
@ -61,19 +79,25 @@ export default class BeastformSheet extends DHBaseItemSheet {
|
|||
|
||||
return data;
|
||||
});
|
||||
context.advantageOn = JSON.stringify(
|
||||
Object.keys(context.document.system.advantageOn).map(key => ({
|
||||
value: key,
|
||||
name: context.document.system.advantageOn[key].value
|
||||
}))
|
||||
);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
async advantageOnAdd(event) {
|
||||
await this.document.update({
|
||||
'system.advantageOn': [...this.document.system.advantageOn, event.detail.data.value]
|
||||
[`system.advantageOn.${foundry.utils.randomID()}`]: { value: event.detail.data.value }
|
||||
});
|
||||
}
|
||||
|
||||
async advantageOnRemove(event) {
|
||||
await this.document.update({
|
||||
'system.advantageOn': this.document.system.advantageOn.filter(x => x !== event.detail.data.value)
|
||||
[`system.advantageOn.-=${event.detail.data.value}`]: null
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,19 +48,29 @@ export default class DhBeastformAction extends DHBaseAction {
|
|||
formData.system.features = [...formData.system.features, ...selectedForm.system.features.map(x => x.uuid)];
|
||||
}
|
||||
|
||||
if (selectedForm.system.beastformType === CONFIG.DH.ITEM.beastformTypes.hybrid.id) {
|
||||
formData.system.advantageOn = Object.values(hybridData.advantages).reduce((advantages, formCategory) => {
|
||||
Object.keys(formCategory).forEach(advantageKey => {
|
||||
advantages[advantageKey] = formCategory[advantageKey];
|
||||
});
|
||||
return advantages;
|
||||
}, {});
|
||||
formData.system.features = [
|
||||
...formData.system.features,
|
||||
...Object.values(hybridData.features).flatMap(x => Object.keys(x))
|
||||
];
|
||||
}
|
||||
|
||||
this.actor.createEmbeddedDocuments('Item', [formData]);
|
||||
}
|
||||
|
||||
async handleActiveTransformations() {
|
||||
const beastformEffects = this.actor.effects.filter(x => x.type === 'beastform');
|
||||
if (beastformEffects.length > 0) {
|
||||
for (let effect of beastformEffects) {
|
||||
await effect.delete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
const existingEffects = beastformEffects.length > 0;
|
||||
await this.actor.deleteEmbeddedDocuments(
|
||||
'ActiveEffect',
|
||||
beastformEffects.map(x => x.id)
|
||||
);
|
||||
return existingEffects;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export default class DHDamageAction extends DHBaseAction {
|
|||
if (isNaN(formula)) formula = Roll.replaceFormulaData(formula, this.getRollData(systemData));
|
||||
|
||||
const config = {
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: this.name }),
|
||||
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: game.i18n.localize(this.name) }),
|
||||
roll: { formula },
|
||||
targets: systemData.targets.filter(t => t.hit) ?? data.targets,
|
||||
hasSave: this.hasSave,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
|
|||
import DhLevelData from '../levelData.mjs';
|
||||
import BaseDataActor from './base.mjs';
|
||||
import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs';
|
||||
import ActionField from '../fields/actionField.mjs';
|
||||
|
||||
export default class DhCharacter extends BaseDataActor {
|
||||
static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Character'];
|
||||
|
|
@ -87,8 +88,45 @@ export default class DhCharacter extends BaseDataActor {
|
|||
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
|
||||
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
|
||||
}),
|
||||
advantageSources: new fields.ArrayField(new fields.StringField()),
|
||||
disadvantageSources: new fields.ArrayField(new fields.StringField()),
|
||||
attack: new ActionField({
|
||||
initial: {
|
||||
name: 'DAGGERHEART.GENERAL.unarmedStrike',
|
||||
img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp',
|
||||
_id: foundry.utils.randomID(),
|
||||
systemPath: 'attack',
|
||||
type: 'attack',
|
||||
range: 'melee',
|
||||
target: {
|
||||
type: 'any',
|
||||
amount: 1
|
||||
},
|
||||
roll: {
|
||||
type: 'attack',
|
||||
trait: 'strength'
|
||||
},
|
||||
damage: {
|
||||
parts: [
|
||||
{
|
||||
type: ['physical'],
|
||||
value: {
|
||||
custom: {
|
||||
enabled: true,
|
||||
formula: '@system.rules.attack.damage.value'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}),
|
||||
advantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.ACTORS.Character.advantageSources.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.advantageSources.hint'
|
||||
}),
|
||||
disadvantageSources: new fields.ArrayField(new fields.StringField(), {
|
||||
label: 'DAGGERHEART.ACTORS.Character.disadvantageSources.label',
|
||||
hint: 'DAGGERHEART.ACTORS.Character.disadvantageSources.hint'
|
||||
}),
|
||||
levelData: new fields.EmbeddedDataField(DhLevelData),
|
||||
bonuses: new fields.SchemaField({
|
||||
roll: new fields.SchemaField({
|
||||
|
|
@ -161,6 +199,15 @@ export default class DhCharacter extends BaseDataActor {
|
|||
magical: new fields.BooleanField({ initial: false }),
|
||||
physical: new fields.BooleanField({ initial: false })
|
||||
}),
|
||||
attack: new fields.SchemaField({
|
||||
damage: new fields.SchemaField({
|
||||
value: new fields.StringField({
|
||||
required: true,
|
||||
initial: '@profd4',
|
||||
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.value.label'
|
||||
})
|
||||
})
|
||||
}),
|
||||
weapon: new fields.SchemaField({
|
||||
/* Unimplemented
|
||||
-> Should remove the lowest damage dice from weapon damage
|
||||
|
|
@ -420,11 +467,14 @@ export default class DhCharacter extends BaseDataActor {
|
|||
const data = super.getRollData();
|
||||
return {
|
||||
...data,
|
||||
...this.resources.tokens,
|
||||
...this.resources.dice,
|
||||
...this.bonuses,
|
||||
tier: this.tier,
|
||||
level: this.levelData.level.current
|
||||
system: {
|
||||
...this.resources.tokens,
|
||||
...this.resources.dice,
|
||||
...this.bonuses,
|
||||
...this.rules,
|
||||
tier: this.tier,
|
||||
level: this.levelData.level.current
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,11 @@ export default class DHBeastform extends BaseDataItem {
|
|||
initial: CONFIG.DH.ACTOR.abilities.agility.id
|
||||
}),
|
||||
examples: new fields.StringField(),
|
||||
advantageOn: new fields.ArrayField(new fields.StringField()),
|
||||
advantageOn: new fields.TypedObjectField(
|
||||
new fields.SchemaField({
|
||||
value: new fields.StringField()
|
||||
})
|
||||
),
|
||||
features: new ForeignDocumentUUIDArrayField({ type: 'Item' }),
|
||||
evolved: new fields.SchemaField({
|
||||
maximumTier: new fields.NumberField({
|
||||
|
|
@ -104,7 +108,13 @@ export default class DHBeastform extends BaseDataItem {
|
|||
await beastformEffect.updateSource({
|
||||
changes: [
|
||||
...beastformEffect.changes,
|
||||
{ key: 'system.advantageSources', mode: 2, value: this.advantageOn.join(', ') }
|
||||
{
|
||||
key: 'system.advantageSources',
|
||||
mode: 2,
|
||||
value: Object.values(this.advantageOn)
|
||||
.map(x => x.value)
|
||||
.join(', ')
|
||||
}
|
||||
],
|
||||
system: {
|
||||
characterTokenData: {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export default class DHToken extends TokenDocument {
|
|||
});
|
||||
bars.sort((a, b) => a.label.compare(b.label));
|
||||
|
||||
const invalidAttributes = ['gold', 'levelData', 'rules.damageReduction.maxArmorMarked.value'];
|
||||
const invalidAttributes = ['gold', 'levelData', 'actions', 'rules.damageReduction.maxArmorMarked.value'];
|
||||
const values = attributes.value.reduce((acc, v) => {
|
||||
const a = v.join('.');
|
||||
if (invalidAttributes.some(x => a.startsWith(x))) return acc;
|
||||
|
|
@ -33,18 +33,20 @@ export default class DHToken extends TokenDocument {
|
|||
return bars.concat(values);
|
||||
}
|
||||
|
||||
static _getTrackedAttributesFromSchema(schema, _path=[]) {
|
||||
const attributes = {bar: [], value: []};
|
||||
for ( const [name, field] of Object.entries(schema.fields) ) {
|
||||
static _getTrackedAttributesFromSchema(schema, _path = []) {
|
||||
const attributes = { bar: [], value: [] };
|
||||
for (const [name, field] of Object.entries(schema.fields)) {
|
||||
const p = _path.concat([name]);
|
||||
if ( field instanceof foundry.data.fields.NumberField ) attributes.value.push(p);
|
||||
if ( field instanceof foundry.data.fields.ArrayField ) attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.NumberField) attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.StringField) attributes.value.push(p);
|
||||
if (field instanceof foundry.data.fields.ArrayField) attributes.value.push(p);
|
||||
const isSchema = field instanceof foundry.data.fields.SchemaField;
|
||||
const isModel = field instanceof foundry.data.fields.EmbeddedDataField;
|
||||
if ( isSchema || isModel ) {
|
||||
|
||||
if (isSchema || isModel) {
|
||||
const schema = isModel ? field.model.schema : field;
|
||||
const isBar = schema.has && schema.has("value") && schema.has("max");
|
||||
if ( isBar ) attributes.bar.push(p);
|
||||
const isBar = schema.has && schema.has('value') && schema.has('max');
|
||||
if (isBar) attributes.bar.push(p);
|
||||
else {
|
||||
const inner = this.getTrackedAttributes(schema, p);
|
||||
attributes.bar.push(...inner.bar);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,26 @@
|
|||
@import '../../utils/colors.less';
|
||||
@import '../../utils/mixin.less';
|
||||
|
||||
.appTheme({
|
||||
&.beastform-selection {
|
||||
.beastforms-outer-container .beastform-title {
|
||||
.theme-light .application.daggerheart.dh-style.views.beastform-selection .beastforms-outer-container {
|
||||
.beastform-title {
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
}
|
||||
|
||||
.advanced-container {
|
||||
.advanced-forms-container {
|
||||
.advanced-form-container {
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
}
|
||||
|
||||
.hybrid-data-wrapper .hybrid-data-container .hybrid-data-inner-container .hybrid-data {
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
}
|
||||
}
|
||||
.form-features .form-feature {
|
||||
background-image: url('../assets/parchments/dh-parchment-light.png');
|
||||
}
|
||||
}
|
||||
}, {});
|
||||
}
|
||||
|
||||
.application.daggerheart.dh-style.views.beastform-selection {
|
||||
.beastforms-outer-container {
|
||||
|
|
@ -80,6 +93,17 @@
|
|||
width: 300px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.advanced-forms-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.advanced-form-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
|
@ -91,11 +115,72 @@
|
|||
height: 120px;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
|
||||
&.hybridized {
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
padding-top: 4px;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
|
||||
&.empty {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.beastform-title {
|
||||
position: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
i {
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.beastform-title-wrapper {
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.hybrid-data-wrapper {
|
||||
overflow: auto;
|
||||
|
||||
.hybrid-data-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding: 0 4px;
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hybrid-data-inner-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
|
||||
.hybrid-data {
|
||||
padding: 0 2px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@dark, @beige);
|
||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||
opacity: 0.4;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,13 +189,13 @@
|
|||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 0 16px;
|
||||
margin-top: 8px;
|
||||
margin: 8px 0;
|
||||
|
||||
.form-feature {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding: 0 2px;
|
||||
padding: 0 4px;
|
||||
border: 1px solid light-dark(@dark-blue, @golden);
|
||||
border-radius: 6px;
|
||||
color: light-dark(@dark, @beige);
|
||||
|
|
|
|||
94
templates/dialogs/beastform/beastformDialog.hbs
Normal file
94
templates/dialogs/beastform/beastformDialog.hbs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
<div>
|
||||
<div class="beastforms-outer-container">
|
||||
<div class="beastforms-container">
|
||||
{{#each beastformTiers as |tier tierKey|}}
|
||||
<fieldset class="beastforms-tier">
|
||||
<legend>{{tier.label}}</legend>
|
||||
{{#each tier.values as |form uuid|}}
|
||||
<div data-action="selectBeastform" data-uuid="{{uuid}}" data-tooltip="{{concat "#item#" uuid}}" class="beastform-container {{#unless form.selected}}inactive{{/unless}} {{#if form.draggable}}draggable{{/if}}">
|
||||
<img src="{{form.value.img}}" />
|
||||
<div class="beastform-title">{{form.value.name}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</fieldset>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="advanced-container {{#if (or (eq selected.system.beastformType 'evolved') (eq selected.system.beastformType 'hybrid'))}}expanded{{/if}}">
|
||||
{{#if (eq selected.system.beastformType 'evolved')}}
|
||||
<h2>{{localize "DAGGERHEART.ITEMS.Beastform.evolve"}}</h2>
|
||||
|
||||
<div class="form-features">
|
||||
{{#if selectedBeastformEffect}}
|
||||
<div class="form-feature" data-tooltip="{{concat "#item#" selectedBeastformEffect.uuid}}">
|
||||
<h4>{{localize "DAGGERHEART.ITEMS.Beastform.evolvedFeatureTitle"}}</h4>
|
||||
<div>{{{selectedBeastformEffect.description}}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="advanced-form-container evolved">
|
||||
{{#if evolved.form}}
|
||||
<div class="beastform-title">{{concat (localize "DAGGERHEART.CONFIG.BeastformType.evolved") " " evolved.form.name}}</div>
|
||||
<img src="{{evolved.form.img}}" />
|
||||
{{else}}
|
||||
<div class="empty-form">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Beastform.evolvedDrag"}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (eq selected.system.beastformType 'hybrid')}}
|
||||
<h2>{{localize "DAGGERHEART.ITEMS.Beastform.hybridize"}}</h2>
|
||||
|
||||
<div class="form-features">
|
||||
{{#if selectedBeastformEffect}}
|
||||
<div class="form-feature" data-tooltip="{{concat "#item#" selectedBeastformEffect.uuid}}">
|
||||
<h4>{{localize "DAGGERHEART.ITEMS.Beastform.hybridizeFeatureTitle"}}</h4>
|
||||
<div>{{{selectedBeastformEffect.description}}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="advanced-forms-container">
|
||||
{{#each hybridForms as | form key |}}
|
||||
<div class="advanced-form-container hybridized {{#unless form}}empty{{/unless}}" id="{{key}}">
|
||||
{{#if form}}
|
||||
<div class="beastform-title-wrapper">
|
||||
<div class="beastform-title">{{form.name}}</div>
|
||||
</div>
|
||||
<div class="hybrid-data-wrapper">
|
||||
<div class="hybrid-data-container">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.features"}}</label>
|
||||
<div class="hybrid-data-inner-container">
|
||||
{{#each form.system.features as | feature |}}
|
||||
<a data-action="toggleHybridFeature" id="{{feature.uuid}}" data-form="{{@../key}}"><div class="hybrid-data {{#if feature.selected}}active{{/if}}" data-tooltip="{{concat "#item#" feature.uuid}}">{{feature.name}}</div></a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hybrid-data-container">
|
||||
<label>{{localize "DAGGERHEART.GENERAL.Advantage.plural"}}</label>
|
||||
<div class="hybrid-data-inner-container">
|
||||
{{#each form.system.advantageOn as | advantage id |}}
|
||||
<a data-action="toggleHybridAdvantage" id="{{id}}" data-form="{{@../key}}"><div class="hybrid-data {{#if advantage.selected}}active{{/if}}">{{advantage.value}}</div></a>
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="empty-form">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Beastform.hybridizeDrag"}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button type="button" data-action="submitBeastform" {{#if (not canSubmit)}}disabled{{/if}}>{{localize "DAGGERHEART.ITEMS.Beastform.transform"}}</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
<div>
|
||||
<div class="beastforms-outer-container">
|
||||
<div class="beastforms-container">
|
||||
{{#each beastformTiers as |tier tierKey|}}
|
||||
<fieldset class="beastforms-tier">
|
||||
<legend>{{tier.label}}</legend>
|
||||
{{#each tier.values as |form uuid|}}
|
||||
<div data-action="selectBeastform" data-uuid="{{uuid}}" data-tooltip="{{concat "#item#" uuid}}" class="beastform-container {{#unless form.selected}}inactive{{/unless}} {{#if form.draggable}}draggable{{/if}}">
|
||||
<img src="{{form.value.img}}" />
|
||||
<div class="beastform-title">{{form.value.name}}</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
</fieldset>
|
||||
{{/each}}
|
||||
</div>
|
||||
<div class="advanced-container {{#if (or (eq selected.system.beastformType 'evolved') (eq selected.system.beastformType 'hybrid'))}}expanded{{/if}}">
|
||||
{{#if (eq selected.system.beastformType 'evolved')}}
|
||||
<h2>{{localize "DAGGERHEART.ITEMS.Beastform.evolve"}}</h2>
|
||||
<div class="advanced-form-container evolved">
|
||||
{{#if evolved.form}}
|
||||
<div class="beastform-title">{{concat (localize "DAGGERHEART.CONFIG.BeastformType.evolved") " " evolved.form.name}}</div>
|
||||
<img src="{{evolved.form.img}}" />
|
||||
{{else}}
|
||||
<div class="empty-form">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
<label>{{localize "Drag a form here to evolve it"}}</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="form-features">
|
||||
{{#if selectedBeastformEffect}}
|
||||
<div class="form-feature" data-tooltip="{{concat "#item#" selectedBeastformEffect.uuid}}">
|
||||
<h4>{{localize "DAGGERHEART.ITEMS.Beastform.evolvedFeatureTitle"}}</h4>
|
||||
<div>{{{selectedBeastformEffect.description}}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button type="button" data-action="submitBeastform" {{#if (not canSubmit)}}disabled{{/if}}>{{localize "DAGGERHEART.ITEMS.Beastform.transform"}}</button>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
</div>
|
||||
{{formGroup settingFields.schema.fields.actionPoints value=settingFields._source.actionPoints localize=true}}
|
||||
s {{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
||||
{{formGroup settingFields.schema.fields.hordeDamage value=settingFields._source.hordeDamage localize=true}}
|
||||
|
||||
<footer class="form-footer">
|
||||
<button data-action="reset">
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@
|
|||
{{formGroup systemFields.mainTrait value=source.system.mainTrait blank=false localize=true}}
|
||||
</div>
|
||||
|
||||
{{formGroup systemFields.examples value=source.system.examples localize=true}}
|
||||
|
||||
<div class="advantage-on-section">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Beastform.FIELDS.advantageOn.label"}}</label>
|
||||
<input class="advantageon-input" value="{{source.system.advantageOn}}" />
|
||||
</div>
|
||||
{{#unless (eq source.system.beastformType 'hybrid')}}
|
||||
{{formGroup systemFields.examples value=source.system.examples localize=true}}
|
||||
<div class="advantage-on-section">
|
||||
<label>{{localize "DAGGERHEART.ITEMS.Beastform.FIELDS.advantageOn.label"}}</label>
|
||||
<input class="advantageon-input" value="{{advantageOn}}" />
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
||||
<fieldset class="two-columns even">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue