From 66defbffce0a89df780acc3ebfb333b025733cf9 Mon Sep 17 00:00:00 2001 From: WBHarry Date: Fri, 30 May 2025 14:16:15 +0200 Subject: [PATCH] Added DomainCard selection --- daggerheart.mjs | 1 + lang/en.json | 30 +++ module/applications/levelup.mjs | 179 ++++++++++++++++-- module/applications/sheets/pc.mjs | 6 +- module/data/levelTier.mjs | 32 +++- module/data/levelup.mjs | 135 ++++++++++--- module/data/pc.mjs | 21 +- module/documents/actor.mjs | 9 + module/helpers/handlebarsHelper.mjs | 49 ++--- styles/daggerheart.css | 135 ++++++++++++- styles/less/global/elements.less | 74 ++++++++ styles/levelup.less | 69 ++++++- templates/components/card-preview.hbs | 13 ++ templates/views/levelup.hbs | 42 ---- .../views/levelup/advancementSelection.hbs | 29 +++ templates/views/levelup/tabs/advancements.hbs | 37 ++++ templates/views/levelup/tabs/selections.hbs | 34 ++++ templates/views/levelup/tabs/summary.hbs | 60 ++++++ 18 files changed, 823 insertions(+), 132 deletions(-) create mode 100644 templates/components/card-preview.hbs delete mode 100644 templates/views/levelup.hbs create mode 100644 templates/views/levelup/advancementSelection.hbs create mode 100644 templates/views/levelup/tabs/advancements.hbs create mode 100644 templates/views/levelup/tabs/selections.hbs create mode 100644 templates/views/levelup/tabs/summary.hbs diff --git a/daggerheart.mjs b/daggerheart.mjs index a431f058..cc923ac6 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -285,6 +285,7 @@ const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/sheets/pc/parts/advancementCard.hbs', 'systems/daggerheart/templates/views/parts/level.hbs', 'systems/daggerheart/templates/components/slider.hbs', + 'systems/daggerheart/templates/components/card-preview.hbs', 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' ]); }; diff --git a/lang/en.json b/lang/en.json index 0addf880..2b5f2724 100755 --- a/lang/en.json +++ b/lang/en.json @@ -142,6 +142,12 @@ "Or": "Or", "Description": "Description", "Features": "Features", + "proficiency": "Proficiency", + "unarmored": "Unarmored", + "Experience": { + "Single": "Experience", + "plural": "Experiences" + }, "RefreshType": { "Session": "Session", "Shortrest": "Short Rest", @@ -744,14 +750,38 @@ "TakeDowntime": "Take Downtime" }, "LevelUp": { + "Title": "{actor} Level Up", + "Tabs": { + "advancement": "Level Advancement", + "selections": "Advancement Choices", + "summary": "Summary" + }, "AdvanceLevel": "Continue To Level {level}", "TakeLevelUp": "Finish Level Up", + "Selections": { + "emptyDomainCardHint": "Domain Card Level {level} or below" + }, + "summary": { + "levelAchievements": "Level Achievements", + "levelAdvancements": "Level Advancements", + "proficiencyIncrease": "Proficiency Increased: {proficiency}", + "statisticIncreases": "Statistic Increases", + "damageThresholdMajorIncrease": "Major: {threshold}", + "damageThresholdSevereIncrease": "Severe: {threshold}", + "newExperiences": "New Experiences", + "experiencePlaceholder": "A new experience..", + "domainCards": "Domain Cards" + }, "notifications": { "info": { "tierAdvancementInfo": "Advancements from a higher tier can always be used to select advancements in a lower tier.", "remainingAdvancementInfo": "Remaining Choices: {choices}", "insufficentAdvancements": "You don't have enough advancements left.", "insufficientTierAdvancements": "You have no available advancements for this tier." + }, + "error": { + "domainCardWrongDomain": "You don't have access to that Domain", + "domainCardToHighLevel": "The Domain Card is too high level to be selected" } } }, diff --git a/module/applications/levelup.mjs b/module/applications/levelup.mjs index c6998523..2aec01a6 100644 --- a/module/applications/levelup.mjs +++ b/module/applications/levelup.mjs @@ -11,13 +11,16 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) const playerLevelupData = actor.system.levelData; this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData, actor.system.level)); + + this._dragDrop = this._createDragDropHandlers(); } get title() { - return `${this.actor.name} - Level Up`; + return game.i18n.format('DAGGERHEART.Application.LevelUp.Title', { actor: this.actor.name }); } static DEFAULT_OPTIONS = { + tag: 'form', classes: ['daggerheart', 'levelup'], position: { width: 1000, height: 'auto' }, window: { @@ -25,31 +28,188 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) }, actions: { save: this.save - } + }, + form: { + handler: this.updateForm, + submitOnChange: true, + closeOnSubmit: false + }, + dragDrop: [{ dragSelector: null, dropSelector: '.levelup-card-selection .card-preview-container' }] }; static PARTS = { - form: { - id: 'levelup', - template: 'systems/daggerheart/templates/views/levelup.hbs' + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + advancements: { template: 'systems/daggerheart/templates/views/levelup/tabs/advancements.hbs' }, + selections: { template: 'systems/daggerheart/templates/views/levelup/tabs/selections.hbs' }, + summary: { template: 'systems/daggerheart/templates/views/levelup/tabs/summary.hbs' } + }; + + static TABS = { + advancements: { + active: true, + cssClass: '', + group: 'primary', + id: 'advancements', + icon: null, + label: 'DAGGERHEART.Application.LevelUp.Tabs.advancement' + }, + selections: { + active: false, + cssClass: '', + group: 'primary', + id: 'selections', + icon: null, + label: 'DAGGERHEART.Application.LevelUp.Tabs.selections' + }, + summary: { + active: false, + cssClass: '', + group: 'primary', + id: 'summary', + icon: null, + label: 'DAGGERHEART.Application.LevelUp.Tabs.summary' } }; async _prepareContext(_options) { const context = await super._prepareContext(_options); context.levelup = this.levelup; + context.tabs = this._getTabs(this.constructor.TABS); return context; } + async _preparePartContext(partId, context) { + switch (partId) { + case 'selections': + context.advancementChoices = this.levelup.selectionData.reduce((acc, data) => { + const advancementChoice = { + ...data, + path: `tiers.${data.tier}.levels.${data.level}.optionSelections.${data.optionKey}.${data.checkboxNr}.data` + }; + if (acc[data.type]) acc[data.type].push(advancementChoice); + else acc[data.type] = [advancementChoice]; + + return acc; + }, {}); + + context.newExperiences = this.levelup.allInitialAchievements.newExperiences; + const allDomainCards = { + ...context.advancementChoices.domainCard, + ...this.levelup.domainCards + }; + const allDomainCardValues = Object.values(allDomainCards); + + context.domainCards = []; + for (var domainCard of allDomainCardValues) { + const uuid = domainCard.data ?? domainCard.uuid; + const card = uuid ? await foundry.utils.fromUuid(uuid) : { path: domainCard.path }; + context.domainCards.push({ + ...(card.toObject?.() ?? card), + emptySubtext: game.i18n.format( + 'DAGGERHEART.Application.LevelUp.Selections.emptyDomainCardHint', + { level: domainCard.level } + ), + limit: domainCard.level + }); + } + + break; + case 'summary': + const actorArmor = this.actor.system.armor; + const { current: currentLevel, changed: changedLevel } = this.actor.system.levelData.level; + context.levelAchievements = { + statisticIncreases: { + proficiency: { + old: this.actor.system.proficiency, + new: this.actor.system.proficiency + this.levelup.allInitialAchievements.proficiency + }, + damageThresholds: { + major: { + old: this.actor.system.damageThresholds.major, + new: this.actor.system.damageThresholds.major + changedLevel - currentLevel + }, + severe: { + old: this.actor.system.damageThresholds.severe, + new: + this.actor.system.damageThresholds.severe + + (actorArmor ? changedLevel - currentLevel : (changedLevel - currentLevel) * 2) + }, + unarmored: !actorArmor + } + } + }; + + break; + } + + return context; + } + + _getTabs(tabs) { + for (const v of Object.values(tabs)) { + v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; + v.cssClass = v.active ? 'active' : ''; + } + + return tabs; + } + + _createDragDropHandlers() { + return this.options.dragDrop.map(d => { + d.callbacks = { + drop: this._onDrop.bind(this) + }; + return new foundry.applications.ux.DragDrop.implementation(d); + }); + } + _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); - $(htmlElement).find('.selection-checkbox').on('change', this.selectionClick.bind(this)); + htmlElement + .querySelectorAll('.selection-checkbox') + .forEach(element => element.addEventListener('change', this.selectionClick.bind(this))); + this._dragDrop.forEach(d => d.bind(htmlElement)); + } + + static async updateForm(event, _, formData) { + const { levelup } = foundry.utils.expandObject(formData.object); + await this.levelup.updateSource(levelup); + this.render(); + } + + async _onDrop(event) { + const data = foundry.applications.ux.TextEditor.getDragEventData(event); + const item = await fromUuid(data.uuid); + if (event.currentTarget.parentElement?.classList?.contains('domain-cards')) { + if (item.type === 'domainCard') { + if (!this.actor.system.class.system.domains.includes(item.system.domain)) { + // Also needs to check for multiclass adding a new domain + ui.notifications.error( + game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.domainCardWrongDomain') + ); + return; + } + + if (item.system.level > Number(event.currentTarget.dataset.limit)) { + ui.notifications.error( + game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.domainCardToHighLevel') + ); + return; + } + + const achievementCard = event.currentTarget.dataset.path.startsWith('domainCards'); + await this.levelup.updateSource({ [event.currentTarget.dataset.path]: item.uuid }); + this.render(); + } + } } async selectionClick(event) { + event.stopPropagation(); const button = event.currentTarget; + // const advancementSelections = this.getAdvancementSelectionUpdates(button); if (!button.checked) { await this.levelup.updateSource({ [`tiers.${button.dataset.tier}.levels.${button.dataset.level}.optionSelections.${button.dataset.option}.-=${button.dataset.checkboxNr}`]: @@ -101,12 +261,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2) } static async save() { - await this.actor.update({ - 'system.levelData': { - 'level.current': this.actor.system.levelData.level.changed, - 'selections': this.levelup.playerData - } - }); + await this.actor.levelUp(this.levelup.selectionData); this.close(); } diff --git a/module/applications/sheets/pc.mjs b/module/applications/sheets/pc.mjs index 63c70e8f..5823a0fe 100644 --- a/module/applications/sheets/pc.mjs +++ b/module/applications/sheets/pc.mjs @@ -182,9 +182,9 @@ export default class PCSheet extends DaggerheartSheet(ActorSheetV2) { htmlElement .querySelectorAll('.experience-value') .forEach(element => element.addEventListener('change', this.experienceValueChange.bind(this))); - htmlElement - .querySelectorAll('[data-item]') - .forEach(element => element.addEventListener.on('change', this.itemUpdate.bind(this))); + // htmlElement + // .querySelectorAll('[data-item]') + // .forEach(element => element.addEventListener.on('change', this.itemUpdate.bind(this))); htmlElement.querySelector('.level-value').addEventListener('change', this.onLevelChange.bind(this)); } diff --git a/module/data/levelTier.mjs b/module/data/levelTier.mjs index 7490f7a5..17e150bf 100644 --- a/module/data/levelTier.mjs +++ b/module/data/levelTier.mjs @@ -61,19 +61,31 @@ class DhLevelOption extends foundry.abstract.DataModel { export const LevelOptionType = { trait: { id: 'trait', - label: 'Character Trait' + label: 'Character Trait', + dataPath: '' }, hitPoint: { id: 'hitPoint', - label: 'Hit Points' + label: 'Hit Points', + dataPath: 'resources.hitPoints', + dataPathData: { + property: 'max', + dependencies: ['value'] + } }, stress: { id: 'stress', - label: 'Stress' + label: 'Stress', + dataPath: 'resources.stress', + dataPathData: { + property: 'max', + dependencies: ['value'] + } }, evasion: { id: 'evasion', - label: 'Evasion' + label: 'Evasion', + dataPath: 'evasion' }, proficiency: { id: 'proficiency', @@ -108,8 +120,8 @@ export const defaultLevelTiers = { }, initialAchievements: { experience: { - nr: 2, - modifier: 1 + nr: 1, + modifier: 2 }, proficiency: 1 }, @@ -171,8 +183,8 @@ export const defaultLevelTiers = { }, initialAchievements: { experience: { - nr: 2, - modifier: 1 + nr: 1, + modifier: 2 }, proficiency: 1 }, @@ -252,8 +264,8 @@ export const defaultLevelTiers = { }, initialAchievements: { experience: { - nr: 2, - modifier: 1 + nr: 1, + modifier: 2 }, proficiency: 1 }, diff --git a/module/data/levelup.mjs b/module/data/levelup.mjs index 0ff522d6..ff2f8295 100644 --- a/module/data/levelup.mjs +++ b/module/data/levelup.mjs @@ -7,17 +7,63 @@ export class DhLevelup extends foundry.abstract.DataModel { const tierKeys = Object.keys(levelTierData.tiers); const maxLevel = levelTierData.tiers[tierKeys[tierKeys.length - 1]].levels.end; - return { - tiers: tierKeys.reduce((acc, key) => { - acc[key] = DhLevelupTier.initializeData( - levelTierData.tiers[key], - maxLevel, - pcLevelData.selections.filter(x => x.tier === Number(key)), - pcLevelData.level.changed - ); + const totalLevelProgression = []; + for (var level = pcLevelData.level.current + 1; level <= pcLevelData.level.changed; level++) { + totalLevelProgression.push(level); + } + + const tiers = tierKeys.reduce((acc, key) => { + acc[key] = DhLevelupTier.initializeData( + levelTierData.tiers[key], + maxLevel, + pcLevelData.selections.filter(x => x.tier === Number(key)), + pcLevelData.level.changed + ); + + return acc; + }, {}); + + const allInitialAchievements = Object.values(tiers).reduce( + (acc, tier) => { + const levelThreshold = Math.min(...tier.belongingLevels); + + if (totalLevelProgression.includes(levelThreshold)) { + acc.proficiency += tier.initialAchievements.proficiency; + [...Array(tier.initialAchievements.experience.nr).keys()].forEach(_ => { + acc.newExperiences[foundry.utils.randomID()] = { + name: '', + modifier: tier.initialAchievements.experience.modifier + }; + }); + } return acc; - }, {}), + }, + { newExperiences: {}, proficiency: 0 } + ); + + const domainCards = Object.keys(tiers).reduce((acc, tierKey) => { + const tier = tiers[tierKey]; + for (var level of tier.belongingLevels) { + if (level <= pcLevelData.level.changed) { + for (var domainCardSlot = 1; domainCardSlot <= tier.domainCardByLevel; domainCardSlot++) { + const cardId = foundry.utils.randomID(); + acc[cardId] = { + uuid: null, + tier: tierKey, + level: level, + domainCardSlot: domainCardSlot, + path: `domainCards.${cardId}.uuid` + }; + } + } + } + + return acc; + }, {}); + + return { + tiers: tiers, maxSelections: [...Array(pcLevelData.level.changed).keys()].reduce((acc, index) => { const level = index + 1; const availableChoices = availableChoicesPerLevel[level]; @@ -26,7 +72,12 @@ export class DhLevelup extends foundry.abstract.DataModel { } return acc; - }, 0) + }, 0), + allInitialAchievements: { + newExperiences: allInitialAchievements.newExperiences, + proficiency: allInitialAchievements.proficiency + }, + domainCards: domainCards }; } @@ -35,7 +86,28 @@ export class DhLevelup extends foundry.abstract.DataModel { return { tiers: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupTier)), - maxSelections: new fields.NumberField({ required: true, integer: true }) + maxSelections: new fields.NumberField({ required: true, integer: true }), + allInitialAchievements: new fields.SchemaField({ + newExperiences: new fields.TypedObjectField( + new fields.SchemaField({ + name: new fields.StringField({ required: true }), + modifier: new fields.NumberField({ required: true, integer: true }) + }) + ), + proficiency: new fields.NumberField({ required: true, integer: true }) + }), + domainCards: new fields.TypedObjectField( + new fields.SchemaField({ + uuid: new fields.StringField({ required: true, nullable: true, initial: null }), + tier: new fields.NumberField({ required: true, integer: true }), + level: new fields.NumberField({ required: true, integer: true }), + domainCardSlot: new fields.NumberField({ required: true, integer: true }), + path: new fields.StringField({ required: true }) + }) + ) + // advancementSelections: new fields.SchemaField({ + // experiences: new fields.SetField(new fields.StringField()), + // }), }; } @@ -57,7 +129,7 @@ export class DhLevelup extends foundry.abstract.DataModel { ); } - get playerData() { + get selectionData() { return Object.keys(this.tiers).flatMap(tierKey => { const tier = this.tiers[tierKey]; return Object.keys(tier.levels).flatMap(levelKey => { @@ -66,15 +138,19 @@ export class DhLevelup extends foundry.abstract.DataModel { const selection = level.optionSelections[optionSelectionKey]; const optionSelect = tier.options[optionSelectionKey]; - return Object.keys(selection).map(checkboxNr => ({ - tier: Number(tierKey), - level: Number(levelKey), - optionKey: optionSelectionKey, - type: optionSelect.type, - checkboxNr: Number(checkboxNr), - value: optionSelect.value, - amount: optionSelect.amount - })); + return Object.keys(selection).map(checkboxNr => { + const selectionObj = selection[checkboxNr]; + return { + tier: Number(tierKey), + level: Number(levelKey), + optionKey: optionSelectionKey, + type: optionSelect.type, + checkboxNr: Number(checkboxNr), + value: optionSelect.value, + amount: optionSelect.amount, + data: selectionObj.data + }; + }); }); }); }); @@ -108,6 +184,8 @@ class DhLevelupTier extends foundry.abstract.DataModel { return acc; }, {}), belongingLevels: belongingLevels, + initialAchievements: levelTier.initialAchievements, + domainCardByLevel: levelTier.domainCardByLevel, levels: levels }; } @@ -121,10 +199,22 @@ class DhLevelupTier extends foundry.abstract.DataModel { active: new fields.BooleanField({ required: true, initial: true }), options: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupTierOption)), belongingLevels: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })), + initialAchievements: new fields.SchemaField({ + experience: new fields.SchemaField({ + nr: new fields.NumberField({ required: true, initial: 1 }), + modifier: new fields.NumberField({ required: true, initial: 2 }) + }), + proficiency: new fields.NumberField({ integer: true, initial: 1 }) + }), + domainCardByLevel: new fields.NumberField({ required: true, integer: true }), levels: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupLevel)) }; } + get initialAchievementData() { + return this.active ? this.initialAchievements : null; + } + get selections() { const allSelections = Object.keys(this.levels).reduce( (acc, key) => { @@ -223,7 +313,8 @@ class DhLevelupLevel extends foundry.abstract.DataModel { new fields.SchemaField({ selected: new fields.BooleanField({ required: true, initial: true }), minCost: new fields.NumberField({ required: true, integer: true }), - locked: new fields.BooleanField({ required: true, initial: false }) + locked: new fields.BooleanField({ required: true, initial: false }), + data: new fields.StringField() }) ) ) diff --git a/module/data/pc.mjs b/module/data/pc.mjs index 96cbef37..62adf232 100644 --- a/module/data/pc.mjs +++ b/module/data/pc.mjs @@ -7,10 +7,7 @@ const attributeField = () => new fields.SchemaField({ data: new fields.SchemaField({ value: new fields.NumberField({ initial: 0, integer: true }), - base: new fields.NumberField({ initial: 0, integer: true }), - bonus: new fields.NumberField({ initial: 0, integer: true }), - actualValue: new fields.NumberField({ initial: 0, integer: true }), - overrideValue: new fields.NumberField({ initial: 0, integer: true }) + bonus: new fields.NumberField({ initial: 0, integer: true }) }) }); @@ -52,11 +49,7 @@ export default class DhpPC extends foundry.abstract.TypeDataModel { presence: attributeField(), knowledge: attributeField() }), - proficiency: new fields.SchemaField({ - value: new fields.NumberField({ initial: 1, integer: true }), - min: new fields.NumberField({ initial: 1, integer: true }), - max: new fields.NumberField({ initial: 6, integer: true }) - }), + proficiency: new fields.NumberField({ required: true, initial: 1, integer: true }), evasion: new fields.NumberField({ initial: 0, integer: true }), experiences: new fields.ArrayField( new fields.SchemaField({ @@ -347,7 +340,15 @@ export default class DhpPC extends foundry.abstract.TypeDataModel { this.evasion = this.class?.system?.evasion ?? 0; // this.armor.value = this.activeArmor?.baseScore ?? 0; - // this.damageThresholds = this.computeDamageThresholds(); + const armor = this.armor; + this.damageThresholds = { + major: armor + ? armor.system.baseThresholds.major + this.levelData.level.current + : this.levelData.level.current, + severe: armor + ? armor.system.baseThresholds.severe + this.levelData.level.current + : this.levelData.level.current * 2 + }; this.applyLevels(); this.applyEffects(); diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 73de0d1f..bef6da88 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -45,6 +45,15 @@ export default class DhpActor extends Actor { } } + async levelUp(levelupData) { + await this.actor.update({ + 'system.levelData': { + 'level.current': this.system.levelData.level.changed, + 'selections': levelupData + } + }); + } + async diceRoll(modifier, shiftKey) { if (this.type === 'pc') { return await this.dualityRoll(modifier, shiftKey); diff --git a/module/helpers/handlebarsHelper.mjs b/module/helpers/handlebarsHelper.mjs index 8a099175..87d1fb7f 100644 --- a/module/helpers/handlebarsHelper.mjs +++ b/module/helpers/handlebarsHelper.mjs @@ -3,22 +3,19 @@ import { getWidthOfText } from './utils.mjs'; export default class RegisterHandlebarsHelpers { static registerHelpers() { Handlebars.registerHelper({ - looseEq: this.looseEq, times: this.times, join: this.join, add: this.add, subtract: this.subtract, objectSelector: this.objectSelector, includes: this.includes, - simpleEditor: this.simpleEditor, - debug: this.debug + debug: this.debug, + signedNumber: this.signedNumber, + switch: this.switch, + case: this.case }); } - static looseEq(a, b) { - return a == b; - } - static times(nr, block) { var accum = ''; for (var i = 0; i < nr; ++i) accum += block.fn(i); @@ -77,33 +74,25 @@ export default class RegisterHandlebarsHelpers { return new Handlebars.SafeString(html); } - static rangePicker(options) { - let { name, value, min, max, step } = options.hash; - name = name || 'range'; - value = value ?? ''; - if (Number.isNaN(value)) value = ''; - const html = ` - ${value}`; - return new Handlebars.SafeString(html); - } - static includes(list, item) { return list.includes(item); } - static simpleEditor(content, options) { - const { - target, - editable = true, - button, - engine = 'tinymce', - collaborate = false, - class: cssClass - } = options.hash; - const config = { name: target, value: content, button, collaborate, editable, engine }; - const element = foundry.applications.fields.createEditorInput(config); - if (cssClass) element.querySelector('.editor-content').classList.add(cssClass); - return new Handlebars.SafeString(element.outerHTML); + static signedNumber(number) { + return number >= 0 ? `+${number}` : number; + } + + static switch(value, options) { + this.switch_value = value; + this.switch_break = false; + return options.fn(this); + } + + static case(value, options) { + if (value == this.switch_value) { + this.switch_break = true; + return options.fn(this); + } } static debug(a) { diff --git a/styles/daggerheart.css b/styles/daggerheart.css index bd3284b2..dea99a6f 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -2791,6 +2791,11 @@ div.daggerheart.views.multiclass { flex-direction: column; gap: 8px; } +.daggerheart.levelup section .section-container { + display: flex; + flex-direction: column; + gap: 8px; +} .daggerheart.levelup .tiers-container { display: flex; gap: 16px; @@ -2844,9 +2849,75 @@ div.daggerheart.views.multiclass { font-size: 14px; font-style: italic; } +.daggerheart.levelup .levelup-selections-container .achievement-experience-cards { + display: flex; + gap: 8px; +} +.daggerheart.levelup .levelup-selections-container .achievement-experience-cards .achievement-experience-card { + border: 1px solid; + border-radius: 4px; + padding-right: 4px; + font-size: 18px; + display: flex; + justify-content: space-between; + align-items: center; + gap: 4px; +} +.daggerheart.levelup .levelup-selections-container .achievement-experience-cards .achievement-experience-card .achievement-experience-marker { + border: 1px solid; + border-radius: 50%; + height: 18px; + width: 18px; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; +} +.daggerheart.levelup .levelup-selections-container .levelup-card-selection { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 40px; + padding-right: 120px; +} +.daggerheart.levelup .levelup-summary-container .level-achievements-container { + display: flex; + flex-direction: column; + gap: 8px; +} +.daggerheart.levelup .levelup-summary-container .level-achievements-container h2, +.daggerheart.levelup .levelup-summary-container .level-achievements-container h3, +.daggerheart.levelup .levelup-summary-container .level-achievements-container h4, +.daggerheart.levelup .levelup-summary-container .level-achievements-container h5 { + margin: 0; + color: var(--color-text-secondary); +} +.daggerheart.levelup .levelup-summary-container .increase-container { + display: flex; + align-items: center; + gap: 4px; + font-size: 20px; +} .daggerheart.levelup .levelup-footer { display: flex; } +.daggerheart.levelup .levelup-footer .advancement-information-container { + display: flex; + gap: 8px; + border-bottom: 3px solid; + border-radius: 4px; + margin-left: 8px; + align-items: center; + padding: 0 8px; + border-color: #9f8475; +} +.daggerheart.levelup .levelup-footer .advancement-information-container .advancement-tier-stats { + border: 1px solid; + padding: 0 2px; + border-radius: 6px; +} +.daggerheart.levelup .levelup-footer .advancement-information-container .advancement-tier-info { + font-size: 14px; +} .application.sheet.daggerheart.dh-style.feature .item-sheet-header { display: flex; } @@ -2892,14 +2963,14 @@ div.daggerheart.views.multiclass { font-style: normal; font-weight: 400; font-display: swap; - src: url(https://fonts.gstatic.com/s/montserrat/v29/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Ew-.ttf) format('truetype'); + src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCtr6Ew-.ttf) format('truetype'); } @font-face { font-family: 'Montserrat'; font-style: normal; font-weight: 600; font-display: swap; - src: url(https://fonts.gstatic.com/s/montserrat/v29/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCu170w-.ttf) format('truetype'); + src: url(https://fonts.gstatic.com/s/montserrat/v30/JTUHjIg1_i6t8kCHKm4532VJOt5-QNFgpCu170w-.ttf) format('truetype'); } .application.sheet.daggerheart.dh-style h1 { font-family: 'Cinzel Decorative', serif; @@ -3167,6 +3238,60 @@ div.daggerheart.views.multiclass { .application.setting.dh-style footer button { flex: 1; } +.theme-light .application .component.dh-style.card.card-preview-container { + background-image: url('../assets/parchments/dh-parchment-light.png'); +} +.theme-light .application .component.dh-style.card.card-preview-container .preview-text-container { + background-image: url(../assets/parchments/dh-parchment-dark.png); +} +.application .component.dh-style.card-preview-container { + border-radius: 6px; + border: 2px solid var(--color-tabs-border); + display: flex; + flex-direction: column; + aspect-ratio: 0.75; + background-image: url('../assets/parchments/dh-parchment-dark.png'); +} +.application .component.dh-style.card-preview-container.empty { + cursor: pointer; +} +.application .component.dh-style.card-preview-container .preview-image-container { + flex: 1; + border-radius: 4px 4px 0; +} +.application .component.dh-style.card-preview-container .preview-text-container { + flex: 1; + border-radius: 0 0 4px 4px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + text-align: center; + color: var(--color-text-selection-bg); + background-image: url(../assets/parchments/dh-parchment-light.png); +} +.application .component.dh-style.card-preview-container .preview-empty-container { + display: flex; + align-items: center; + justify-content: center; + flex: 1; +} +.application .component.dh-style.card-preview-container .preview-empty-container .preview-empty-inner-container { + position: relative; + width: 100%; + display: flex; + justify-content: center; +} +.application .component.dh-style.card-preview-container .preview-empty-container .preview-empty-inner-container .preview-add-icon { + font-size: 48px; +} +.application .component.dh-style.card-preview-container .preview-empty-container .preview-empty-inner-container .preview-empty-subtext { + position: absolute; + bottom: -48px; + font-size: 18px; + font-variant: small-caps; + text-align: center; +} .sheet.daggerheart.dh-style .tab-navigation { margin: 5px 0; height: 40px; @@ -3399,6 +3524,12 @@ div.daggerheart.views.multiclass { /* Flex */ /****/ } +.daggerheart .vertical-separator { + border-left: 2px solid black; + height: 56px; + flex: 0; + align-self: center; +} .daggerheart .flex-centered { display: flex; align-items: center; diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less index 9001e65e..3fcd8765 100755 --- a/styles/less/global/elements.less +++ b/styles/less/global/elements.less @@ -212,3 +212,77 @@ } } } + +.theme-light { + .application { + .component.dh-style.card { + &.card-preview-container { + background-image: url('../assets/parchments/dh-parchment-light.png'); + + .preview-text-container { + background-image: url(../assets/parchments/dh-parchment-dark.png); + } + } + } + } +} + +.application { + .component.dh-style { + &.card-preview-container { + border-radius: 6px; + border: 2px solid var(--color-tabs-border); + display: flex; + flex-direction: column; + aspect-ratio: 0.75; + background-image: url('../assets/parchments/dh-parchment-dark.png'); + + &.empty { + cursor: pointer; + } + + .preview-image-container { + flex: 1; + border-radius: 4px 4px 0; + } + + .preview-text-container { + flex: 1; + border-radius: 0 0 4px 4px; + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + text-align: center; + color: var(--color-text-selection-bg); + background-image: url(../assets/parchments/dh-parchment-light.png); + } + + .preview-empty-container { + display: flex; + align-items: center; + justify-content: center; + flex: 1; + + .preview-empty-inner-container { + position: relative; + width: 100%; + display: flex; + justify-content: center; + + .preview-add-icon { + font-size: 48px; + } + + .preview-empty-subtext { + position: absolute; + bottom: -48px; + font-size: 18px; + font-variant: small-caps; + text-align: center; + } + } + } + } + } +} diff --git a/styles/levelup.less b/styles/levelup.less index 6c9d4935..115b1d2b 100644 --- a/styles/levelup.less +++ b/styles/levelup.less @@ -9,6 +9,14 @@ gap: 8px; } + section { + .section-container { + display: flex; + flex-direction: column; + gap: 8px; + } + } + .tiers-container { display: flex; gap: 16px; @@ -73,13 +81,72 @@ } } + .levelup-selections-container { + .achievement-experience-cards { + display: flex; + gap: 8px; + + .achievement-experience-card { + border: 1px solid; + border-radius: 4px; + padding-right: 4px; + font-size: 18px; + display: flex; + justify-content: space-between; + align-items: center; + gap: 4px; + + .achievement-experience-marker { + border: 1px solid; + border-radius: 50%; + height: 18px; + width: 18px; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + } + } + } + + .levelup-card-selection { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 40px; + padding-right: 120px; + } + } + + .levelup-summary-container { + .level-achievements-container { + display: flex; + flex-direction: column; + gap: 8px; + + h2, + h3, + h4, + h5 { + margin: 0; + color: var(--color-text-secondary); + } + } + + .increase-container { + display: flex; + align-items: center; + gap: 4px; + font-size: 20px; + } + } + .levelup-footer { display: flex; .advancement-information-container { display: flex; gap: 8px; - border: 1px solid; + border-bottom: 3px solid; border-radius: 4px; margin-left: 8px; align-items: center; diff --git a/templates/components/card-preview.hbs b/templates/components/card-preview.hbs new file mode 100644 index 00000000..b62b6ab5 --- /dev/null +++ b/templates/components/card-preview.hbs @@ -0,0 +1,13 @@ +
+ {{#if this.img}} + +
{{this.name}}
+ {{else}} +
+
+ +
{{this.emptySubtext}}
+
+
+ {{/if}} +
\ No newline at end of file diff --git a/templates/views/levelup.hbs b/templates/views/levelup.hbs deleted file mode 100644 index 49c9c0fd..00000000 --- a/templates/views/levelup.hbs +++ /dev/null @@ -1,42 +0,0 @@ -
-
- {{#each this.levelup.tiers as |tier key|}} -
- {{tier.name}} - - {{#each tier.tierCheckboxGroups}} -
-
- {{#each this.checkboxGroups}} -
- {{#each this.checkboxes}} - - {{/each}} -
- {{/each}} -
-
{{this.label}}
-
- {{/each}} -
- {{/each}} -
-
- -
- {{localize "DAGGERHEART.Application.LevelUp.notifications.info.remainingAdvancementInfo" choices=this.levelup.levelSelections.totalAvailable}} - - {{#each this.levelup.tiers as |tier key|}} -
Tier {{tier.tier}}: {{tier.selections.totalAvailable}}
- {{/each}} - -
-
-
\ No newline at end of file diff --git a/templates/views/levelup/advancementSelection.hbs b/templates/views/levelup/advancementSelection.hbs new file mode 100644 index 00000000..5d646693 --- /dev/null +++ b/templates/views/levelup/advancementSelection.hbs @@ -0,0 +1,29 @@ +{{#switch}} + {{#case 'trait'}} + + {{/case}} +{{/switch}} + + + +trait: { + id: 'trait', + label: 'Character Trait', + dataPath: '' +}, +experience: { + id: 'experience', + label: 'Experience' +}, +domainCard: { + id: 'domainCard', + label: 'Domain Card' +}, +subclass: { + id: 'subclass', + label: 'Subclass' +}, +multiclass: { + id: 'multiclass', + label: 'Multiclass' +} \ No newline at end of file diff --git a/templates/views/levelup/tabs/advancements.hbs b/templates/views/levelup/tabs/advancements.hbs new file mode 100644 index 00000000..2b372db3 --- /dev/null +++ b/templates/views/levelup/tabs/advancements.hbs @@ -0,0 +1,37 @@ +
+
+
+ {{#each this.levelup.tiers as |tier key|}} +
+ {{tier.name}} + + {{#each tier.tierCheckboxGroups}} +
+
+ {{#each this.checkboxGroups}} +
+ {{#each this.checkboxes}} + + {{/each}} +
+ {{/each}} +
+
{{this.label}}
+
+ {{/each}} +
+ {{/each}} +
+
+
\ No newline at end of file diff --git a/templates/views/levelup/tabs/selections.hbs b/templates/views/levelup/tabs/selections.hbs new file mode 100644 index 00000000..7b0a1ebd --- /dev/null +++ b/templates/views/levelup/tabs/selections.hbs @@ -0,0 +1,34 @@ +
+
+
+

{{localize "DAGGERHEART.Application.LevelUp.summary.newExperiences"}}

+
+ {{#each this.newExperiences as |experience key|}} +
+
+ +
{{signedNumber experience.modifier}}
+
+
+ {{#if experience.name}}{{/if}} +
+
+ {{/each}} +
+
+ +
+

{{localize "DAGGERHEART.Application.LevelUp.summary.domainCards"}}

+ +
+ {{#each this.domainCards}} + {{> "systems/daggerheart/templates/components/card-preview.hbs" img=this.img name=this.name path=this.path }} + {{/each}} +
+
+
+
\ No newline at end of file diff --git a/templates/views/levelup/tabs/summary.hbs b/templates/views/levelup/tabs/summary.hbs new file mode 100644 index 00000000..ddacb3f0 --- /dev/null +++ b/templates/views/levelup/tabs/summary.hbs @@ -0,0 +1,60 @@ +
+
+
+ {{localize "DAGGERHEART.Application.LevelUp.summary.levelAchievements"}} + +
+
+

{{localize "DAGGERHEART.Application.LevelUp.summary.statisticIncreases"}}

+
+ {{localize "DAGGERHEART.Application.LevelUp.summary.proficiencyIncrease" proficiency=this.levelAchievements.statisticIncreases.proficiency.old }} + + {{this.levelAchievements.statisticIncreases.proficiency.new}} +
+
{{localize "Damage Thresholds"}}{{#if this.levelAchievements.statisticIncreases.damageThresholds.unarmored}}({{localize "DAGGERHEART.General.unarmored"}}){{/if}}
+
+ {{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdMajorIncrease" threshold=this.levelAchievements.statisticIncreases.damageThresholds.major.old }} + + {{this.levelAchievements.statisticIncreases.damageThresholds.major.new}} +
+
+ {{localize "DAGGERHEART.Application.LevelUp.summary.damageThresholdSevereIncrease" threshold=this.levelAchievements.statisticIncreases.damageThresholds.severe.old }} + + {{this.levelAchievements.statisticIncreases.damageThresholds.severe.new}} +
+
+
+
+ +
+ {{localize "DAGGERHEART.Application.LevelUp.summary.levelAdvancements"}} + + {{!--
+

{{localize "DAGGERHEART.General.Experience.plural"}}

+
+ {{#each this.levelAchievements.experience}} +
+ {{this.label}} +
+ {{/each}} +
+
--}} +
+ + +
+
\ No newline at end of file