diff --git a/daggerheart.mjs b/daggerheart.mjs index dbe5aa21..071a8bce 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -79,6 +79,7 @@ Hooks.once('init', () => { Items.registerSheet(SYSTEM.id, applications.DhpConsumable, { types: ['consumable'], makeDefault: true }); Items.registerSheet(SYSTEM.id, applications.DhpWeapon, { types: ['weapon'], makeDefault: true }); Items.registerSheet(SYSTEM.id, applications.DhpArmor, { types: ['armor'], makeDefault: true }); + Items.registerSheet(SYSTEM.id, applications.DhBeastform, { types: ['beastform'], makeDefault: true }); CONFIG.Actor.documentClass = documents.DhpActor; CONFIG.Actor.dataModels = models.actors.config; diff --git a/lang/en.json b/lang/en.json index 2277f6ab..30673555 100755 --- a/lang/en.json +++ b/lang/en.json @@ -10,7 +10,8 @@ "consumable": "Consumable", "miscellaneous": "Miscellaneous", "weapon": "Weapon", - "armor": "Armor" + "armor": "Armor", + "beastform": "Beastform" }, "Actor": { "character": "Character", @@ -1461,8 +1462,18 @@ } } }, + "Beastform": { + "DialogTitle": "Beastform Selection", + "FIELDS": { + "tier": { "label": "Tier" }, + "examples": { "label": "Examples" }, + "advantageOn": { "label": "Gain Advantage On" } + }, + "Transform": "Transform" + }, "Global": { "Actions": "Actions", + "Features": "Features", "Effects": "Effects", "activeEffects": "Active Effects", "inativeEffects": "Inative Effects" diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs index a74cb8cf..a2b82a75 100644 --- a/module/applications/_module.mjs +++ b/module/applications/_module.mjs @@ -15,5 +15,6 @@ export { default as DhpChatMessage } from './chatMessage.mjs'; export { default as DhpEnvironment } from './sheets/actors/environment.mjs'; export { default as DhActiveEffectConfig } from './sheets/activeEffectConfig.mjs'; export { default as DhContextMenu } from './contextMenu.mjs'; +export { default as DhBeastform } from './sheets/items/beastform.mjs'; export * as api from './sheets/api/_modules.mjs'; diff --git a/module/applications/sheets/items/beastform.mjs b/module/applications/sheets/items/beastform.mjs new file mode 100644 index 00000000..12134558 --- /dev/null +++ b/module/applications/sheets/items/beastform.mjs @@ -0,0 +1,62 @@ +import DHBaseItemSheet from '../api/base-item.mjs'; + +export default class BeastformSheet extends DHBaseItemSheet { + /**@inheritdoc */ + static DEFAULT_OPTIONS = { + classes: ['beastform'], + dragDrop: [{ dragSelector: null, dropSelector: '.drop-section' }], + actions: { + editFeature: this.editFeature, + removeFeature: this.removeFeature + } + }; + + /**@override */ + static PARTS = { + header: { template: 'systems/daggerheart/templates/sheets/items/beastform/header.hbs' }, + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + settings: { template: 'systems/daggerheart/templates/sheets/items/beastform/settings.hbs' }, + features: { + template: 'systems/daggerheart/templates/sheets/global/tabs/tab-features.hbs', + scrollable: ['.features'] + } + }; + + static TABS = { + primary: { + tabs: [{ id: 'settings' }, { id: 'features' }], + initial: 'settings', + labelPrefix: 'DAGGERHEART.Sheets.TABS' + } + }; + + /**@inheritdoc */ + async _preparePartContext(partId, context) { + await super._preparePartContext(partId, context); + + return context; + } + + static editFeature(event) { + const target = event.target.closest('[data-action="editFeature"]'); + const feature = this.document.system.features[target.dataset.index]; + feature.sheet.render(true); + } + + static async removeFeature(_, target) { + const current = this.document.system.features.map(x => x.uuid); + await this.document.update({ + 'system.features': current.filter((_, index) => index !== Number(target.dataset.index)) + }); + this.render(); + } + + async _onDrop(event) { + const data = TextEditor.getDragEventData(event); + const item = await fromUuid(data.uuid); + if (item.type === 'feature') { + const current = this.document.system.features.map(x => x.uuid); + await this.document.update({ 'system.features': [...current, item.uuid] }); + } + } +} diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 6710d3ed..06c75fce 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -245,19 +245,23 @@ export const deathMoves = { export const tiers = { tier1: { id: 'tier1', - label: 'DAGGERHEART.Tiers.tier1' + label: 'DAGGERHEART.Tiers.tier1', + value: 1 }, tier2: { id: 'tier2', - label: 'DAGGERHEART.Tiers.tier2' + label: 'DAGGERHEART.Tiers.tier2', + value: 2 }, tier3: { id: 'tier3', - label: 'DAGGERHEART.Tiers.tier3' + label: 'DAGGERHEART.Tiers.tier3', + value: 3 }, tier4: { id: 'tier4', - label: 'DAGGERHEART.Tiers.tier4' + label: 'DAGGERHEART.Tiers.tier4', + value: 4 } }; diff --git a/module/data/action/action.mjs b/module/data/action/action.mjs index 00f3ef2c..eac05a7f 100644 --- a/module/data/action/action.mjs +++ b/module/data/action/action.mjs @@ -2,6 +2,7 @@ import CostSelectionDialog from '../../applications/costSelectionDialog.mjs'; import { DHActionDiceData, DHActionRollData, DHDamageData, DHDamageField } from './actionDice.mjs'; import DhpActor from '../../documents/actor.mjs'; import D20RollDialog from '../../dialogs/d20RollDialog.mjs'; +import BeastformDialog from '../../dialogs/beastformDialog.mjs'; const fields = foundry.data.fields; @@ -271,7 +272,8 @@ export class DHBaseAction extends foundry.abstract.DataModel { } if (this instanceof DhBeastformAction) { - console.log('Test'); + config = await BeastformDialog.configure(config); + if (!config) return; } if (this.doFollowUp()) { diff --git a/module/data/item/_module.mjs b/module/data/item/_module.mjs index da3bf2d4..a29d1595 100644 --- a/module/data/item/_module.mjs +++ b/module/data/item/_module.mjs @@ -8,6 +8,7 @@ import DHFeature from './feature.mjs'; import DHMiscellaneous from './miscellaneous.mjs'; import DHSubclass from './subclass.mjs'; import DHWeapon from './weapon.mjs'; +import DHBeastform from './beastform.mjs'; export { DHAncestry, @@ -19,7 +20,8 @@ export { DHFeature, DHMiscellaneous, DHSubclass, - DHWeapon + DHWeapon, + DHBeastform }; export const config = { @@ -32,5 +34,6 @@ export const config = { feature: DHFeature, miscellaneous: DHMiscellaneous, subclass: DHSubclass, - weapon: DHWeapon + weapon: DHWeapon, + beastform: DHBeastform }; diff --git a/module/data/item/beastform.mjs b/module/data/item/beastform.mjs new file mode 100644 index 00000000..8df5dd0e --- /dev/null +++ b/module/data/item/beastform.mjs @@ -0,0 +1,32 @@ +import ActionField from '../fields/actionField.mjs'; +import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; +import BaseDataItem from './base.mjs'; + +export default class DHBeastform extends BaseDataItem { + static LOCALIZATION_PREFIXES = ['DAGGERHEART.Sheets.Beastform']; + + /** @inheritDoc */ + static get metadata() { + return foundry.utils.mergeObject(super.metadata, { + label: 'TYPES.Item.beastform', + type: 'beastform', + hasDescription: false + }); + } + + /** @inheritDoc */ + static defineSchema() { + const fields = foundry.data.fields; + return { + ...super.defineSchema(), + tier: new fields.StringField({ + required: true, + choices: SYSTEM.GENERAL.tiers, + initial: SYSTEM.GENERAL.tiers.tier1.id + }), + examples: new fields.StringField(), + advantageOn: new fields.ArrayField(new fields.StringField()), + features: new ForeignDocumentUUIDArrayField({ type: 'Item' }) + }; + } +} diff --git a/module/dialogs/beastformDialog.mjs b/module/dialogs/beastformDialog.mjs new file mode 100644 index 00000000..d9699b59 --- /dev/null +++ b/module/dialogs/beastformDialog.mjs @@ -0,0 +1,83 @@ +import { tiers } from '../config/generalConfig.mjs'; + +const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + +export default class BeastformDialog extends HandlebarsApplicationMixin(ApplicationV2) { + constructor(configData) { + super(); + + this.configData = configData; + this.selected = null; + } + + static DEFAULT_OPTIONS = { + tag: 'form', + id: 'beastform-selection', + classes: ['daggerheart', 'views', 'dh-style', 'beastform-selection'], + position: { + width: 600, + height: 'auto' + }, + actions: { + submitBeastform: this.submitBeastform + }, + form: { + handler: this.updateBeastform, + submitOnChange: true, + submitOnClose: false + } + }; + + get title() { + return game.i18n.localize('DAGGERHEART.Sheets.Beastform.DialogTitle'); + } + + /** @override */ + static PARTS = { + beastform: { + template: 'systems/daggerheart/templates/views/beastformDialog.hbs' + } + }; + + async _prepareContext(_options) { + const context = await super._prepareContext(_options); + context.configData = this.configData; + + const tierLimit = 2; + context.beastformTiers = game.items.reduce((acc, x) => { + const tier = tiers[x.system.tier]; + if (x.type !== 'beastform' || tier.value > tierLimit) return acc; + + if (!acc[tier.value]) acc[tier.value] = { label: game.i18n.localize(tier.label), values: {} }; + acc[tier.value].values[x.uuid] = { selected: this.selected == x.uuid, value: x }; + + return acc; + }, {}); // Also get from compendium when added + context.canSubmit = this.selected; + + return context; + } + + static updateBeastform(event, _, formData) { + this.selected = foundry.utils.mergeObject(this.selected, formData.object); + + this.render(); + } + + static async submitBeastform() { + await this.close({ submitted: true }); + } + + /** @override */ + _onClose(options = {}) { + if (!options.submitted) this.config = false; + } + + static async configure(configData) { + return new Promise(resolve => { + const app = new this(configData); + app.addEventListener('close', () => resolve(app.config), { once: true }); + app.render({ force: true }); + }); + } +} diff --git a/styles/daggerheart.css b/styles/daggerheart.css index 062dd5a3..73b4750c 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -4996,6 +4996,53 @@ div.daggerheart.views.multiclass { color: light-dark(#18162e50, #efe6d850); font-family: 'Montserrat', sans-serif; } +.theme-light .application.daggerheart.dh-style.views.beastform-selection .beastforms-container .beastforms-tier .beastform-container .beastform-title { + background-image: url('../assets/parchments/dh-parchment-dark.png'); +} +.application.daggerheart.dh-style.views.beastform-selection .beastforms-container { + display: flex; + flex-direction: column; + gap: 4px; +} +.application.daggerheart.dh-style.views.beastform-selection .beastforms-container .beastforms-tier { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 4px; +} +.application.daggerheart.dh-style.views.beastform-selection .beastforms-container .beastforms-tier .beastform-container { + position: relative; + display: flex; + justify-content: center; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + cursor: pointer; +} +.application.daggerheart.dh-style.views.beastform-selection .beastforms-container .beastforms-tier .beastform-container.disabled { + opacity: 0.4; +} +.application.daggerheart.dh-style.views.beastform-selection .beastforms-container .beastforms-tier .beastform-container img { + width: 100%; + border-radius: 6px; +} +.application.daggerheart.dh-style.views.beastform-selection .beastforms-container .beastforms-tier .beastform-container .beastform-title { + position: absolute; + top: 4px; + display: flex; + flex-wrap: wrap; + font-size: 16px; + margin: 0 4px; + border: 1px solid light-dark(#18162e, #f3c267); + border-radius: 6px; + color: light-dark(#efe6d8, #222); + background-image: url('../assets/parchments/dh-parchment-light.png'); +} +.application.daggerheart.dh-style.views.beastform-selection footer { + margin-top: 8px; + display: flex; +} +.application.daggerheart.dh-style.views.beastform-selection footer button { + flex: 1; +} .application.sheet.daggerheart.actor.dh-style.companion .profile { height: 80px; width: 80px; @@ -5703,6 +5750,44 @@ div.daggerheart.views.multiclass { justify-content: center; gap: 10px; } +.sheet.daggerheart.dh-style .tab.features .features-list { + display: flex; + flex-direction: column; + list-style: none; + padding: 0; + margin: 0; + width: 100%; + gap: 5px; +} +.sheet.daggerheart.dh-style .tab.features .features-list .feature-item { + display: grid; + align-items: center; + grid-template-columns: 1fr 4fr 1fr; + cursor: pointer; +} +.sheet.daggerheart.dh-style .tab.features .features-list .feature-item img { + height: 40px; + width: 40px; + object-fit: cover; + border-radius: 3px; +} +.sheet.daggerheart.dh-style .tab.features .features-list .feature-item h4 { + font-family: 'Montserrat', sans-serif; + font-weight: lighter; + color: #efe6d8; +} +.sheet.daggerheart.dh-style .tab.features .features-list .feature-item .image { + height: 40px; + width: 40px; + object-fit: cover; + border-radius: 6px; + border: none; +} +.sheet.daggerheart.dh-style .tab.features .features-list .feature-item .controls { + display: flex; + justify-content: center; + gap: 10px; +} .sheet.daggerheart.dh-style .tab.effects .effects-list { display: flex; flex-direction: column; diff --git a/styles/daggerheart.less b/styles/daggerheart.less index 362856ce..b915d44a 100755 --- a/styles/daggerheart.less +++ b/styles/daggerheart.less @@ -40,6 +40,8 @@ @import './less/applications/environment-settings/actions.less'; @import './less/applications/environment-settings/adversaries.less'; +@import './less/applications//beastform.less'; + @import './less/actors/companion/sheet.less'; @import './less/actors/adversary.less'; @@ -58,6 +60,7 @@ @import './less/global/tab-navigation.less'; @import './less/global/tab-form-footer.less'; @import './less/global/tab-actions.less'; +@import './less/global/tab-features.less'; @import './less/global/tab-effects.less'; @import './less/global/item-header.less'; @import './less/global/feature-section.less'; diff --git a/styles/less/applications/beastform.less b/styles/less/applications/beastform.less new file mode 100644 index 00000000..e809bedd --- /dev/null +++ b/styles/less/applications/beastform.less @@ -0,0 +1,59 @@ +.theme-light .application.daggerheart.dh-style.views.beastform-selection { + .beastforms-container .beastforms-tier .beastform-container .beastform-title { + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } +} + +.application.daggerheart.dh-style.views.beastform-selection { + .beastforms-container { + display: flex; + flex-direction: column; + gap: 4px; + + .beastforms-tier { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 4px; + + .beastform-container { + position: relative; + display: flex; + justify-content: center; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + cursor: pointer; + + &.disabled { + opacity: 0.4; + } + + img { + width: 100%; + border-radius: 6px; + } + + .beastform-title { + position: absolute; + top: 4px; + display: flex; + flex-wrap: wrap; + font-size: 16px; + margin: 0 4px; + border: 1px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + color: light-dark(@beige, @dark); + background-image: url('../assets/parchments/dh-parchment-light.png'); + } + } + } + } + + footer { + margin-top: 8px; + display: flex; + + button { + flex: 1; + } + } +} diff --git a/styles/less/global/tab-features.less b/styles/less/global/tab-features.less new file mode 100644 index 00000000..91335fd5 --- /dev/null +++ b/styles/less/global/tab-features.less @@ -0,0 +1,50 @@ +@import '../utils/colors.less'; +@import '../utils/fonts.less'; + +.sheet.daggerheart.dh-style { + .tab.features { + .features-list { + display: flex; + flex-direction: column; + list-style: none; + padding: 0; + margin: 0; + width: 100%; + gap: 5px; + + .feature-item { + display: grid; + align-items: center; + grid-template-columns: 1fr 4fr 1fr; + cursor: pointer; + + img { + height: 40px; + width: 40px; + object-fit: cover; + border-radius: 3px; + } + + h4 { + font-family: @font-body; + font-weight: lighter; + color: @beige; + } + + .image { + height: 40px; + width: 40px; + object-fit: cover; + border-radius: 6px; + border: none; + } + + .controls { + display: flex; + justify-content: center; + gap: 10px; + } + } + } + } +} diff --git a/system.json b/system.json index 9b22d117..864e540e 100644 --- a/system.json +++ b/system.json @@ -247,7 +247,8 @@ }, "armor": { "htmlFields": ["description"] - } + }, + "beastform": {} }, "Combat": { "combat": {} diff --git a/templates/sheets/global/partials/inventory-fieldset-items.hbs b/templates/sheets/global/partials/inventory-fieldset-items.hbs index 0051d6df..cd99fe17 100644 --- a/templates/sheets/global/partials/inventory-fieldset-items.hbs +++ b/templates/sheets/global/partials/inventory-fieldset-items.hbs @@ -14,11 +14,6 @@ {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=action type=../type}} {{/if}} {{/each}} - {{#each document.system.ancestry.system.actions as |action|}} - {{#if (or (eq ../type 'ancestry'))}} - {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=action type=../type}} - {{/if}} - {{/each}} {{#each document.system.class.value.system.classFeatures as |classFeature|}} {{#if (or (eq ../type 'class'))}} {{> 'systems/daggerheart/templates/sheets/global/partials/inventory-item.hbs' item=classFeature type=../type}} diff --git a/templates/sheets/global/tabs/tab-features.hbs b/templates/sheets/global/tabs/tab-features.hbs new file mode 100644 index 00000000..59a43655 --- /dev/null +++ b/templates/sheets/global/tabs/tab-features.hbs @@ -0,0 +1,23 @@ +
+
+ {{localize "DAGGERHEART.Sheets.Global.Features"}} +
+ {{#each document.system.features as |feature index|}} +
+ + {{feature.name}} +
+ +
+
+ {{/each}} +
+
+
\ No newline at end of file diff --git a/templates/sheets/items/beastform/header.hbs b/templates/sheets/items/beastform/header.hbs new file mode 100644 index 00000000..9c116c13 --- /dev/null +++ b/templates/sheets/items/beastform/header.hbs @@ -0,0 +1,10 @@ +
+ +
+ +

+
+

{{localize 'TYPES.Item.beastform'}}

+
+
+
\ No newline at end of file diff --git a/templates/sheets/items/beastform/settings.hbs b/templates/sheets/items/beastform/settings.hbs new file mode 100644 index 00000000..68dc8d39 --- /dev/null +++ b/templates/sheets/items/beastform/settings.hbs @@ -0,0 +1,16 @@ +
+
+ {{formGroup systemFields.tier value=source.system.tier localize=true}} + {{formGroup systemFields.examples value=source.system.examples localize=true}} +
+ +
+ {{localize "DAGGERHEART.Sheets.Beastform.FIELDS.advantageOn.label"}} + + {{!-- {{formGroup systemFields.examples value=source.system.examples localize=true}} --}} +
+
\ No newline at end of file diff --git a/templates/views/beastformDialog.hbs b/templates/views/beastformDialog.hbs new file mode 100644 index 00000000..adb0c01b --- /dev/null +++ b/templates/views/beastformDialog.hbs @@ -0,0 +1,18 @@ +
+
+ {{#each beastformTiers as |tier tierKey|}} +
+ {{tier.label}} + {{#each tier.values as |form uuid|}} +
{{!-- data-tooltip="{{concat "#item#" uuid}}" --}} + +
{{form.value.name}}
+
+ {{/each}} +
+ {{/each}} +
+ +
\ No newline at end of file