diff --git a/assets/icons/documents/actors/dark-squad.svg b/assets/icons/documents/actors/dark-squad.svg new file mode 100644 index 00000000..f21b4c5b --- /dev/null +++ b/assets/icons/documents/actors/dark-squad.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/daggerheart.mjs b/daggerheart.mjs index 795764cc..88a03bae 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -93,6 +93,10 @@ Hooks.once('init', () => { types: ['environment'], makeDefault: true }); + Actors.registerSheet(SYSTEM.id, applications.sheets.actors.Party, { + types: ['party'], + makeDefault: true + }); CONFIG.ActiveEffect.documentClass = documents.DhActiveEffect; CONFIG.ActiveEffect.dataModels = models.activeEffects.config; diff --git a/lang/en.json b/lang/en.json index a7a615f5..12b452df 100755 --- a/lang/en.json +++ b/lang/en.json @@ -20,7 +20,8 @@ "character": "Character", "companion": "Companion", "adversary": "Adversary", - "environment": "Environment" + "environment": "Environment", + "party": "Party" } }, "CONTROLS": { @@ -1890,7 +1891,8 @@ "tier4": "tier 4", "domains": "Domains", "downtime": "Downtime", - "rules": "Rules" + "rules": "Rules", + "partyMembers": "Party Members" }, "Tiers": { "singular": "Tier", diff --git a/module/applications/sheets/actors/_module.mjs b/module/applications/sheets/actors/_module.mjs index 9998733c..c4ea2d94 100644 --- a/module/applications/sheets/actors/_module.mjs +++ b/module/applications/sheets/actors/_module.mjs @@ -2,3 +2,4 @@ export { default as Adversary } from './adversary.mjs'; export { default as Character } from './character.mjs'; export { default as Companion } from './companion.mjs'; export { default as Environment } from './environment.mjs'; +export { default as Party } from './party.mjs'; diff --git a/module/applications/sheets/actors/party.mjs b/module/applications/sheets/actors/party.mjs new file mode 100644 index 00000000..98dd90d7 --- /dev/null +++ b/module/applications/sheets/actors/party.mjs @@ -0,0 +1,122 @@ +import DHBaseActorSheet from '../api/base-actor.mjs'; + +export default class Party extends DHBaseActorSheet { + /**@inheritdoc */ + static DEFAULT_OPTIONS = { + classes: ['party'], + position: { + width: 500 + }, + window: { + resizable: true + }, + actions: {}, + dragDrop: [{ dragSelector: '.actors-section .inventory-item', dropSelector: null }] + }; + + /**@override */ + static PARTS = { + header: { template: 'systems/daggerheart/templates/sheets/actors/party/header.hbs' }, + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + partyMembers: { template: 'systems/daggerheart/templates/sheets/actors/party/party-members.hbs' }, + notes: { template: 'systems/daggerheart/templates/sheets/actors/party/notes.hbs' } + }; + + /** @inheritdoc */ + static TABS = { + primary: { + tabs: [{ id: 'partyMembers' }, { id: 'notes' }], + initial: 'partyMembers', + labelPrefix: 'DAGGERHEART.GENERAL.Tabs' + } + }; + + async _preparePartContext(partId, context, options) { + context = await super._preparePartContext(partId, context, options); + switch (partId) { + case 'header': + await this._prepareHeaderContext(context, options); + break; + case 'notes': + await this._prepareNotesContext(context, options); + break; + } + return context; + } + + /** + * Prepare render context for the Header part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareHeaderContext(context, _options) { + const { system } = this.document; + const { TextEditor } = foundry.applications.ux; + + context.description = await TextEditor.implementation.enrichHTML(system.description, { + secrets: this.document.isOwner, + relativeTo: this.document + }); + } + + /** + * Prepare render context for the Biography part. + * @param {ApplicationRenderContext} context + * @param {ApplicationRenderOptions} options + * @returns {Promise} + * @protected + */ + async _prepareNotesContext(context, _options) { + const { system } = this.document; + const { TextEditor } = foundry.applications.ux; + + const paths = { + notes: 'notes' + }; + + for (const [key, path] of Object.entries(paths)) { + const value = foundry.utils.getProperty(system, path); + context[key] = { + field: system.schema.getField(path), + value, + enriched: await TextEditor.implementation.enrichHTML(value, { + secrets: this.document.isOwner, + relativeTo: this.document + }) + }; + } + } + + /* -------------------------------------------- */ + + async _onDragStart(event) { + const item = event.currentTarget.closest('.inventory-item'); + + if (item) { + const adversaryData = { type: 'Actor', uuid: item.dataset.itemUuid }; + event.dataTransfer.setData('text/plain', JSON.stringify(adversaryData)); + event.dataTransfer.setDragImage(item, 60, 0); + } + } + + async _onDrop(event) { + const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); + if (data.fromInternal) return; + + const item = await fromUuid(data.uuid); + if (item.type === 'adversary' && event.target.closest('.category-container')) { + const target = event.target.closest('.category-container'); + const path = `system.potentialAdversaries.${target.dataset.potentialAdversary}.adversaries`; + const current = foundry.utils.getProperty(this.actor, path).map(x => x.uuid); + await this.actor.update({ + [path]: [...current, item.uuid] + }); + this.render(); + } else if (item.type === 'feature' && event.target.closest('.tab.features')) { + await this.actor.createEmbeddedDocuments('Item', [item]); + this.render(); + } + } +} diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 21f1feae..d156f166 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -473,6 +473,7 @@ export default function DHApplicationMixin(Base) { context.fields = this.document.schema.fields; context.systemFields = this.document.system.schema.fields; context.settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); + console.log(context); return context; } diff --git a/module/data/actor/_module.mjs b/module/data/actor/_module.mjs index c19036eb..99577620 100644 --- a/module/data/actor/_module.mjs +++ b/module/data/actor/_module.mjs @@ -2,12 +2,14 @@ import DhCharacter from './character.mjs'; import DhCompanion from './companion.mjs'; import DhAdversary from './adversary.mjs'; import DhEnvironment from './environment.mjs'; +import DhParty from './party.mjs'; -export { DhCharacter, DhCompanion, DhAdversary, DhEnvironment }; +export { DhCharacter, DhCompanion, DhAdversary, DhEnvironment, DhParty }; export const config = { character: DhCharacter, companion: DhCompanion, adversary: DhAdversary, - environment: DhEnvironment + environment: DhEnvironment, + party: DhParty }; diff --git a/module/data/actor/party.mjs b/module/data/actor/party.mjs new file mode 100644 index 00000000..2b65122b --- /dev/null +++ b/module/data/actor/party.mjs @@ -0,0 +1,26 @@ +import BaseDataActor from './base.mjs'; +import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; + +export default class DhParty extends BaseDataActor { + /**@inheritdoc */ + static defineSchema() { + const fields = foundry.data.fields; + return { + ...super.defineSchema(), + partyMembers: new fields.TypedObjectField( + new fields.SchemaField({ + label: new fields.StringField(), + adversaries: new ForeignDocumentUUIDArrayField({ type: 'Actor' }) + }) + ), + notes: new fields.HTMLField() + }; + } + + /* -------------------------------------------- */ + + /**@inheritdoc */ + static DEFAULT_ICON = 'systems/daggerheart/assets/icons/documents/actors/dark-squad.svg'; + + /* -------------------------------------------- */ +} diff --git a/styles/less/sheets/actors/party/header.less b/styles/less/sheets/actors/party/header.less new file mode 100644 index 00000000..9a2c7350 --- /dev/null +++ b/styles/less/sheets/actors/party/header.less @@ -0,0 +1,42 @@ +@import '../../../utils/colors.less'; +@import '../../../utils/fonts.less'; + +.party-header-sheet { + display: flex; + flex-direction: column; + justify-content: start; + text-align: center; + + .profile { + height: 235px; + mask-image: linear-gradient(0deg, transparent 0%, black 10%); + cursor: pointer; + } + + .item-container { + .item-name { + padding: 0 20px; + input[type='text'] { + font-size: 32px; + height: 42px; + text-align: center; + transition: all 0.3s ease; + outline: 2px solid transparent; + border: 1px solid transparent; + + &:hover[type='text'], + &:focus[type='text'] { + box-shadow: none; + outline: 2px solid light-dark(@dark-blue, @golden); + } + } + } + + .label { + font-style: normal; + font-weight: 700; + font-size: 16px; + margin: 5px 0; + } + } +} diff --git a/styles/less/sheets/actors/party/sheet.less b/styles/less/sheets/actors/party/sheet.less new file mode 100644 index 00000000..faa9412c --- /dev/null +++ b/styles/less/sheets/actors/party/sheet.less @@ -0,0 +1,28 @@ +@import '../../../utils/colors.less'; +@import '../../../utils/fonts.less'; +@import '../../../utils/mixin.less'; + +.appTheme({ + &.party { + background-image: url('../assets/parchments/dh-parchment-dark.png'); + } +}, { + &.party { + background: url('../assets/parchments/dh-parchment-light.png'); + } +}); + +.application.sheet.daggerheart.actor.dh-style.party { + .tab { + max-height: 300px; + overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: light-dark(@dark-blue, @golden) transparent; + + &.active { + overflow: hidden; + display: flex; + flex-direction: column; + } + } +} diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index 1ffb92fe..9c4307f8 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -22,6 +22,9 @@ @import './actors/environment/header.less'; @import './actors/environment/sheet.less'; +@import './actors/party/header.less'; +@import './actors/party/sheet.less'; + @import './items/beastform.less'; @import './items/class.less'; @import './items/domain-card.less'; diff --git a/system.json b/system.json index e6b7650f..f7a6422f 100644 --- a/system.json +++ b/system.json @@ -220,6 +220,9 @@ }, "environment": { "htmlFields": ["notes", "description"] + }, + "party": { + "htmlFields": ["notes"] } }, "Item": { diff --git a/templates/sheets/actors/party/header.hbs b/templates/sheets/actors/party/header.hbs new file mode 100644 index 00000000..f39f683f --- /dev/null +++ b/templates/sheets/actors/party/header.hbs @@ -0,0 +1,9 @@ +
+ +
+
+

+

Party

+
+
+
\ No newline at end of file diff --git a/templates/sheets/actors/party/notes.hbs b/templates/sheets/actors/party/notes.hbs new file mode 100644 index 00000000..663a484a --- /dev/null +++ b/templates/sheets/actors/party/notes.hbs @@ -0,0 +1,10 @@ +
+
+ {{localize tabs.notes.label}} + {{formInput notes.field value=notes.value enriched=notes.value toggled=true}} +
+
\ No newline at end of file diff --git a/templates/sheets/actors/party/party-members.hbs b/templates/sheets/actors/party/party-members.hbs new file mode 100644 index 00000000..1594302f --- /dev/null +++ b/templates/sheets/actors/party/party-members.hbs @@ -0,0 +1,19 @@ +
+
+ {{#each document.system.partyMembers as |category categoryId|}} + {{> 'daggerheart.inventory-items' + title=tabs.partyMembers.label + type='character' + isGlassy=true + isActor=true + hideControls=true + collection=category.adversaries + hideResources=true + }} + {{/each}} +
+
\ No newline at end of file