This commit is contained in:
WBHarry 2026-04-20 04:05:37 -04:00 committed by GitHub
commit be6c10395a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 260 additions and 16 deletions

View file

@ -682,6 +682,9 @@
"title": "{actor} Level Up",
"viewModeTitle": "{actor} Level Up (View Mode)"
},
"LevelupOptionsDialog": {
"title": "Levelup Options: {name}"
},
"MulticlassChoice": {
"title": "Multiclassing - {actor}",
"explanation": "You are adding {class} as your multiclass",
@ -1263,6 +1266,10 @@
"diceValue": "Dice Value",
"die": "Die"
},
"LevelupData": {
"checkboxSelections": "Checkboxes",
"minCost": "Cost Per Checkbox"
},
"Range": {
"self": {
"name": "Self",
@ -3257,6 +3264,7 @@
"rightClickExtend": "Right-Click to extend",
"companionPartnerLevelBlock": "The companion needs an assigned partner to level up.",
"configureAttribution": "Configure Attribution",
"configureLevelupOptions": "Configure Levelup Options",
"deleteItem": "Delete Item",
"immune": "Immune",
"middleClick": "[Middle Click] Keep tooltip view",

View file

@ -17,3 +17,4 @@ export { default as TagTeamDialog } from './tagTeamDialog.mjs';
export { default as GroupRollDialog } from './groupRollDialog.mjs';
export { default as RiskItAllDialog } from './riskItAllDialog.mjs';
export { default as CompendiumBrowserSettingsDialog } from './CompendiumBrowserSettings.mjs';
export { default as LevelupOptionsDialog } from './levelupOptionsDialog.mjs';

View file

@ -0,0 +1,92 @@
import { LevelOptionType } from "../../data/levelTier.mjs";
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class LevelupOptionsDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(item) {
super({});
this.item = item;
this.selectedOption = null;
}
get title() {
return game.i18n.format('DAGGERHEART.APPLICATIONS.LevelupOptionsDialog.title', { name: this.item.name });
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'levelup-options-dialog'],
position: { width: 480, height: 'auto' },
window: { icon: 'fa-solid fa-angles-up fa-fw' },
actions: {
addTierOption: LevelupOptionsDialog.#addTierOption,
removeTierOption: LevelupOptionsDialog.#removeTierOption,
},
form: { handler: this.updateData, submitOnChange: true, closeOnSubmit: false }
};
static PARTS = {
header: { template: 'systems/daggerheart/templates/dialogs/levelupOptionsDialog/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
tiers: { template: 'systems/daggerheart/templates/dialogs/levelupOptionsDialog/tiers.hbs' },
};
/** @inheritdoc */
static TABS = {
primary: {
tabs: [
{ id: 'tier2', label: 'DAGGERHEART.GENERAL.Tiers.2', tier: 2 },
{ id: 'tier3', label: 'DAGGERHEART.GENERAL.Tiers.3', tier: 3 },
{ id: 'tier4', label: 'DAGGERHEART.GENERAL.Tiers.4', tier: 4 }
],
initial: 'tier2',
}
};
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
for(const element of htmlElement.querySelectorAll('.option-type-select'))
element.addEventListener('change', this.updateSelectedOption.bind(this));
}
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.item = this.item;
context.fields = this.item.system.schema.fields.levelupOptionTiers.element.element.fields;
context.optionTypes = LevelOptionType;
context.selectedOption = this.selectedOption;
return context;
}
static async updateData(_event, _element, formData) {
const data = foundry.utils.expandObject(formData.object);
this.render();
}
updateSelectedOption(event) {
this.selectedOption = event.target.value;
this.render();
}
static async #addTierOption(_event, button) {
const { tier } = button.dataset;
await this.item.update({ [`system.levelupOptionTiers.${tier}.${foundry.utils.randomID()}`]: {
label: LevelOptionType[this.selectedOption].label,
type: this.selectedOption
}});
this.selectedOption = null;
this.render();
}
static async #removeTierOption(_event, button) {
const { tier, key } = button.dataset;
await this.item.update({ [`system.levelupOptionTiers.${tier}.${key}`]: _del });
this.render();
}
}

View file

@ -6,9 +6,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
constructor(actor) {
super(actor);
this.levelTiers = this.addBonusChoices(
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers)
);
this.levelTiers = this.addBonusChoices(actor.system.levelupTiers);
const playerLevelupData = actor.system.levelData;
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
}
@ -366,7 +364,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
advancement.experience?.flatMap(x => x.data.map(data => ({ name: data, modifier: x.value }))) ??
[],
multiclass: advancement.multiclass,
subclass: advancement.subclass
subclass: advancement.subclass,
};
context.advancements.statistics.proficiency.shown =

View file

@ -99,7 +99,8 @@ export default function DHApplicationMixin(Base) {
toggleExtended: DHSheetV2.#toggleExtended,
addNewItem: DHSheetV2.#addNewItem,
browseItem: DHSheetV2.#browseItem,
editAttribution: DHSheetV2.#editAttribution
editAttribution: DHSheetV2.#editAttribution,
configureLevelUpOptions: DHSheetV2.#configureLevelUpOptions,
},
contextMenus: [
{
@ -119,6 +120,16 @@ export default function DHApplicationMixin(Base) {
}
}
],
window: {
controls: [
{
icon: 'fa-solid fa-angles-up fa-fw',
label: 'DAGGERHEART.UI.Tooltip.configureLevelupOptions',
action: 'configureLevelUpOptions',
visible: DHSheetV2.#hasLevelUpOptions,
}
],
},
dragDrop: [{ dragSelector: '.inventory-item[data-type="effect"]', dropSelector: null }],
tagifyConfigs: []
};
@ -142,6 +153,10 @@ export default function DHApplicationMixin(Base) {
return frame;
}
static #hasLevelUpOptions() {
return this.document.system.metadata.hasLevelUpOptions;
};
/**
* Refresh the custom parts of the application frame
*/
@ -708,6 +723,10 @@ export default function DHApplicationMixin(Base) {
new game.system.api.applications.dialogs.AttributionDialog(this.document).render({ force: true });
}
static async #configureLevelUpOptions() {
new game.system.api.applications.dialogs.LevelupOptionsDialog(this.document).render({ force: true });
}
/**
* Create an embedded document.
* @type {ApplicationClickAction}

View file

@ -303,6 +303,12 @@ export default class DhCharacter extends DhCreature {
initial: null,
label: 'DAGGERHEART.ACTORS.Character.defaultDisadvantageDice'
}),
}),
comboDieIndex: new fields.NumberField({
integer: true,
min: 0,
max: 5,
initial: 0,
})
})
};
@ -447,6 +453,19 @@ export default class DhCharacter extends DhCreature {
return attack;
}
get levelupTiers() {
const tierData = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers);
for (const tierKey of Object.keys(this.class?.value?.system.levelupOptionTiers ?? {})) {
const tier = this.class.value.system.levelupOptionTiers[tierKey];
for (const optionKey of Object.keys(tier)) {
const option = tier[optionKey];
tierData.tiers[tierKey].options[optionKey] = option;
}
}
return tierData;
}
/* All items are valid on characters */
isItemValid() {
return true;
@ -745,6 +764,9 @@ export default class DhCharacter extends DhCreature {
}
});
break;
case 'comboStrikes':
this.rules.comboDieIndex += 1;
break;
}
}
}

View file

@ -3,6 +3,7 @@ import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs';
import ItemLinkFields from '../fields/itemLinkFields.mjs';
import { addLinkedItemsDiff, getFeaturesHTMLData, updateLinkedItemApps } from '../../helpers/utils.mjs';
import { DhLevelOption } from '../levelTier.mjs';
export default class DHClass extends BaseDataItem {
/** @inheritDoc */
@ -10,7 +11,8 @@ export default class DHClass extends BaseDataItem {
return foundry.utils.mergeObject(super.metadata, {
label: 'TYPES.Item.class',
type: 'class',
hasDescription: true
hasDescription: true,
hasLevelUpOptions: true,
});
}
@ -51,7 +53,10 @@ export default class DHClass extends BaseDataItem {
}),
backgroundQuestions: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }),
connections: new fields.ArrayField(new fields.StringField(), { initial: ['', '', ''] }),
isMulticlass: new fields.BooleanField({ initial: false })
isMulticlass: new fields.BooleanField({ initial: false }),
levelupOptionTiers: new fields.TypedObjectField(new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelOption)), {
initial: { 2: {}, 3: {}, 4: {} }
}),
};
}

View file

@ -43,17 +43,40 @@ class DhLevelTier extends foundry.abstract.DataModel {
}
}
class DhLevelOption extends foundry.abstract.DataModel {
export class DhLevelOption extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
label: new fields.StringField({ required: true }),
checkboxSelections: new fields.NumberField({ required: true, integer: true, initial: 1 }),
minCost: new fields.NumberField({ required: true, integer: true, initial: 1 }),
type: new fields.StringField({ required: true, choices: LevelOptionType }),
value: new fields.NumberField({ integer: true }),
amount: new fields.NumberField({ integer: true })
label: new fields.StringField({
required: true,
label: 'DAGGERHEART.GENERAL.label'
}),
checkboxSelections: new fields.NumberField({
required: true,
integer: true,
initial: 1,
label: 'DAGGERHEART.CONFIG.LevelupData.checkboxSelections'
}),
minCost: new fields.NumberField({
required: true,
integer: true,
initial: 1,
label: 'DAGGERHEART.CONFIG.LevelupData.minCost'
}),
type: new fields.StringField({
required: true,
choices: LevelOptionType,
label: 'DAGGERHEART.GENERAL.type'
}),
value: new fields.NumberField({
integer: true,
label: 'DAGGERHEART.GENERAL.value'
}),
amount: new fields.NumberField({
integer: true,
label: 'DAGGERHEART.GENERAL.amount'
})
};
}
}
@ -113,6 +136,13 @@ export const CompanionLevelOptionType = {
}
};
export const ClassLevelOptionTypes = {
comboStrikes: {
id: 'comboStrikes',
label: 'Increase your Combo Die size',
},
};
export const LevelOptionType = {
trait: {
id: 'trait',
@ -162,6 +192,7 @@ export const LevelOptionType = {
id: 'multiclass',
label: 'Multiclass'
},
...ClassLevelOptionTypes,
...CompanionLevelOptionType
};

View file

@ -51,6 +51,7 @@ export const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/ui/itemBrowser/itemContainer.hbs',
'systems/daggerheart/templates/scene/dh-config.hbs',
'systems/daggerheart/templates/settings/appearance-settings/diceSoNiceTab.hbs',
'systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs'
'systems/daggerheart/templates/sheets/activeEffect/typeChanges/armorChange.hbs',
'systems/daggerheart/templates/dialogs/levelupOptionsDialog/parts/tier.hbs'
]);
};

View file

@ -51,3 +51,4 @@
@import './character-reset/sheet.less';
@import './compendiumBrowserPackDialog/sheet.less';
@import './levelup-options-dialog/sheet.less';

View file

@ -0,0 +1,32 @@
.daggerheart.dh-style.dialog.levelup-options-dialog {
.dialog-title {
font-size: var(--font-size-32);
color: light-dark(@dark-blue, @golden);
text-align: center;
}
.tier-tools {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
button {
white-space: nowrap;
}
}
.tier-container {
padding: 0 4px;
display: flex;
flex-direction: column;
gap: 8px;
.tier-title {
font-size: var(--font-size-18);
color: light-dark(@dark-blue, @golden);
margin-left: auto;
margin-right: auto;
}
}
}

View file

@ -0,0 +1,3 @@
<div>
<div class="dialog-title">{{localize "Tiers"}}</div>
</div>

View file

@ -0,0 +1,26 @@
<section class="tab scrollable {{tab.cssClass}} {{tab.id}}" data-group="{{tab.group}}" data-tab="{{tab.id}}">
<div class="tier-tools">
<select class="option-type-select">
{{selectOptions optionTypes selected=selectedOption blank=""}}
</select>
<button data-action="addTierOption" data-tier="{{tab.tier}}" {{#unless selectedOption}}disabled{{/unless}}>{{localize "Add Levelup Option"}}</button>
</div>
{{#with (lookup item.system.levelupOptionTiers tab.tier)}}
{{#unless (empty this)}}
{{#each this as |option key|}}
<fieldset>
<legend><a data-action="removeTierOption" data-tier="{{../../tab.tier}}" data-key="{{key}}"><i class="fa-solid fa-trash"></i></a></legend>
<div class="tier-container">
{{formGroup @root.fields.label value=option.label name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".label") localize=true }}
{{formGroup @root.fields.type value=option.type name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".type") localize=true }}
<div class="two-columns even">
{{formGroup @root.fields.checkboxSelections value=option.checkboxSelections name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".checkboxSelections") localize=true }}
{{formGroup @root.fields.minCost value=option.minCost name=(concat "system.levelOptionTiers." ../../tab.tier "." key ".minCost") localize=true }}
</div>
</div>
</fieldset>
{{/each}}
{{/unless}}
{{/with}}
</section>

View file

@ -0,0 +1,5 @@
<div>
{{> "systems/daggerheart/templates/dialogs/levelupOptionsDialog/parts/tier.hbs" tab=tabs.tier2}}
{{> "systems/daggerheart/templates/dialogs/levelupOptionsDialog/parts/tier.hbs" tab=tabs.tier3}}
{{> "systems/daggerheart/templates/dialogs/levelupOptionsDialog/parts/tier.hbs" tab=tabs.tier4}}
<div>