This commit is contained in:
WBHarry 2025-08-06 10:56:21 +02:00
parent 0bb20b2ddc
commit d01965e5b9
20 changed files with 146 additions and 67 deletions

View file

@ -2148,7 +2148,7 @@
"newDomain": "New Domain", "newDomain": "New Domain",
"editDomain": "Active Domain", "editDomain": "Active Domain",
"deleteDomain": "Delete Domain", "deleteDomain": "Delete Domain",
"deleteDomainText": "Are you sure you want to delete the {name} domain?" "deleteDomainText": "Are you sure you want to delete the {name} domain? It will be removed from all Actors in this world where it's currently used."
} }
}, },
"Menu": { "Menu": {

View file

@ -34,7 +34,7 @@ export default class MulticlassChoiceDialog extends HandlebarsApplicationMixin(A
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.multiclass = this.multiclass; context.multiclass = this.multiclass;
context.domainChoices = this.multiclass.domains.map(value => { context.domainChoices = this.multiclass.domains.map(value => {
const domain = CONFIG.DH.DOMAIN.domains[value]; const domain = CONFIG.DH.DOMAIN.allDomains()[value];
return { return {
value: value, value: value,
label: game.i18n.localize(domain.label), label: game.i18n.localize(domain.label),

View file

@ -1,6 +1,5 @@
import LevelUpBase from './levelup.mjs'; import LevelUpBase from './levelup.mjs';
import { DhLevelup } from '../../data/levelup.mjs'; import { DhLevelup } from '../../data/levelup.mjs';
import { domains } from '../../config/domainConfig.mjs';
import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs'; import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs';
export default class DhCharacterLevelUp extends LevelUpBase { export default class DhCharacterLevelUp extends LevelUpBase {
@ -113,7 +112,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
: levelBase; : levelBase;
return game.i18n.format('DAGGERHEART.APPLICATIONS.Levelup.selections.emptyDomainCardHint', { return game.i18n.format('DAGGERHEART.APPLICATIONS.Levelup.selections.emptyDomainCardHint', {
domain: game.i18n.localize(domains[domain.domain].label), domain: game.i18n.localize(CONFIG.DH.DOMAIN.allDomains()[domain.domain].label),
level: levelMax level: levelMax
}); });
}), }),
@ -170,7 +169,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
uuid: multiclass.uuid, uuid: multiclass.uuid,
domains: domains:
multiclass?.system?.domains.map(key => { multiclass?.system?.domains.map(key => {
const domain = domains[key]; const domain = CONFIG.DH.DOMAIN.allDomains()[key];
const alreadySelected = this.actor.system.class.value.system.domains.includes(key); const alreadySelected = this.actor.system.class.value.system.domains.includes(key);
return { return {
@ -315,7 +314,10 @@ export default class DhCharacterLevelUp extends LevelUpBase {
? { ? {
...multiclassItem.toObject(), ...multiclassItem.toObject(),
domain: checkbox.secondaryData.domain domain: checkbox.secondaryData.domain
? game.i18n.localize(domains[checkbox.secondaryData.domain].label) ? game.i18n.localize(
CONFIG.DH.DOMAIN.allDomains()[checkbox.secondaryData.domain]
.label
)
: null, : null,
subclass: subclass ? subclass.name : null subclass: subclass ? subclass.name : null
} }

View file

@ -1,5 +1,4 @@
import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs'; import { abilities, subclassFeatureLabels } from '../../config/actorConfig.mjs';
import { domains } from '../../config/domainConfig.mjs';
import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs'; import { getDeleteKeys, tagifyElement } from '../../helpers/utils.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -249,7 +248,10 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
? { ? {
...multiclassItem.toObject(), ...multiclassItem.toObject(),
domain: checkbox.secondaryData.domain domain: checkbox.secondaryData.domain
? game.i18n.localize(domains[checkbox.secondaryData.domain].label) ? game.i18n.localize(
CONFIG.DH.DOMAIN.allDomains()[checkbox.secondaryData.domain]
.label
)
: null, : null,
subclass: subclass ? subclass.name : null subclass: subclass ? subclass.name : null
} }
@ -353,10 +355,10 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
experienceIncreaseTagify, experienceIncreaseTagify,
Object.keys(this.actor.system.experiences).reduce((acc, id) => { Object.keys(this.actor.system.experiences).reduce((acc, id) => {
const experience = this.actor.system.experiences[id]; const experience = this.actor.system.experiences[id];
acc[id] = { label: experience.name }; acc.push({ id: id, label: experience.name });
return acc; return acc;
}, {}), }, []),
this.tagifyUpdate('experience').bind(this) this.tagifyUpdate('experience').bind(this)
); );
} }

View file

@ -83,7 +83,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
return context; return context;
} }
static async updateData(event, element, formData) { static async updateData(_event, _element, formData) {
const updatedSettings = foundry.utils.expandObject(formData.object); const updatedSettings = foundry.utils.expandObject(formData.object);
await this.settings.updateSource({ await this.settings.updateSource({
@ -199,7 +199,7 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
this.render(); this.render();
} }
static async addDomain(_, target) { static async addDomain() {
const id = foundry.utils.randomID(); const id = foundry.utils.randomID();
this.settings.updateSource({ this.settings.updateSource({
[`domains.${id}`]: { [`domains.${id}`]: {
@ -219,6 +219,40 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
} }
static async deleteDomain() { static async deleteDomain() {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.localize('DAGGERHEART.SETTINGS.Homebrew.domains.deleteDomain')
},
content: game.i18n.format('DAGGERHEART.SETTINGS.Homebrew.domains.deleteDomainText', {
name: this.settings.domains[this.selected.domain].label
})
});
if (!confirmed) return;
const updateClasses = game.items.filter(
x => x.type === 'class' && x.system.domains.includes(this.selected.domain)
);
for (let actor of game.actors) {
updateClasses.push(
...actor.items.filter(x => x.type === 'class' && x.system.domains.includes(this.selected.domain))
);
}
for (let c of updateClasses) {
const newDomains = c.system.domains.map(x =>
x === this.selected.domain ? CONFIG.DH.DOMAIN.domains.arcana.id : x
);
await c.update({ 'system.domains': newDomains });
}
const updateDomainCards = game.items.filter(
x => x.type === 'domainCard' && x.system.domain === this.selected.domain
);
for (let d of updateDomainCards) {
await d.update({ 'system.domain': CONFIG.DH.DOMAIN.domains.arcana.id });
}
await this.settings.updateSource({ await this.settings.updateSource({
[`domains.-=${this.selected.domain}`]: null [`domains.-=${this.selected.domain}`]: null
}); });

View file

@ -14,7 +14,7 @@ export default class ClassSheet extends DHBaseItemSheet {
tagifyConfigs: [ tagifyConfigs: [
{ {
selector: '.domain-input', selector: '.domain-input',
options: () => CONFIG.DH.DOMAIN.domains, options: () => CONFIG.DH.DOMAIN.orderedDomains(),
callback: ClassSheet.#onDomainSelect, callback: ClassSheet.#onDomainSelect,
tagifyOptions: { tagifyOptions: {
maxTags: () => game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxDomains maxTags: () => game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxDomains

View file

@ -34,4 +34,12 @@ export default class DomainCardSheet extends DHBaseItemSheet {
scrollable: ['.effects'] scrollable: ['.effects']
} }
}; };
async _prepareContext(options) {
const context = await super._prepareContext(options);
context.domain = CONFIG.DH.DOMAIN.allDomains()[this.document.system.domain];
context.domainChoices = CONFIG.DH.DOMAIN.orderedDomains();
return context;
}
} }

View file

@ -219,7 +219,7 @@ export default class FilterMenu extends foundry.applications.ux.ContextMenu {
} }
})); }));
const domainFilter = Object.values(CONFIG.DH.DOMAIN.domains).map(({ id, label }) => ({ const domainFilter = Object.values(CONFIG.DH.DOMAIN.allDomains()).map(({ id, label }) => ({
group: game.i18n.localize('DAGGERHEART.GENERAL.Domain.single'), group: game.i18n.localize('DAGGERHEART.GENERAL.Domain.single'),
name: game.i18n.localize(label), name: game.i18n.localize(label),
filter: { filter: {

View file

@ -55,8 +55,17 @@ export const domains = {
} }
}; };
export const classDomainMap = { export const allDomains = () => ({
rogue: [domains.midnight, domains.grace] ...domains,
...game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).domains
});
export const orderedDomains = () => {
const all = {
...domains,
...game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).domains
};
return Object.values(all).sort((a, b) => game.i18n.localize(a.label).localeCompare(game.i18n.localize(b.label)));
}; };
export const subclassMap = { export const subclassMap = {

View file

@ -18,7 +18,7 @@ export default class DHDomainCard extends BaseDataItem {
return { return {
...super.defineSchema(), ...super.defineSchema(),
domain: new fields.StringField({ domain: new fields.StringField({
choices: CONFIG.DH.DOMAIN.domains, choices: CONFIG.DH.DOMAIN.allDomains(),
required: true, required: true,
initial: CONFIG.DH.DOMAIN.domains.arcana.id initial: CONFIG.DH.DOMAIN.domains.arcana.id
}), }),

View file

@ -83,15 +83,16 @@ export const chunkify = (array, chunkSize, mappingFunc) => {
return chunkifiedArray; return chunkifiedArray;
}; };
export const tagifyElement = (element, options, onChange, tagifyOptions = {}) => { export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {}) => {
const { maxTags } = tagifyOptions; const { maxTags } = tagifyOptions;
const options = typeof baseOptions === 'object' ? Object.values(baseOptions) : baseOptions;
const tagifyElement = new Tagify(element, { const tagifyElement = new Tagify(element, {
tagTextProp: 'name', tagTextProp: 'name',
enforceWhitelist: true, enforceWhitelist: true,
whitelist: Object.keys(options).map(key => { whitelist: options.map(option => {
const option = options[key];
return { return {
value: key, value: option.id,
name: game.i18n.localize(option.label), name: game.i18n.localize(option.label),
src: option.src, src: option.src,
description: option.description description: option.description
@ -100,7 +101,7 @@ export const tagifyElement = (element, options, onChange, tagifyOptions = {}) =>
maxTags: typeof maxTags === 'function' ? maxTags() : maxTags, maxTags: typeof maxTags === 'function' ? maxTags() : maxTags,
dropdown: { dropdown: {
mapValueTo: 'name', mapValueTo: 'name',
searchKeys: ['name'], searchKeys: ['value'],
enabled: 0, enabled: 0,
maxItems: 100, maxItems: 100,
closeOnSelect: true, closeOnSelect: true,

View file

@ -619,6 +619,7 @@
margin-left: 8px; margin-left: 8px;
height: 20px; height: 20px;
width: 20px; width: 20px;
filter: @dark-filter;
} }
} }
} }
@ -651,7 +652,8 @@
} }
} }
.theme-light .application { .theme-light {
.application {
&.sheet.dh-style { &.sheet.dh-style {
button.glow { button.glow {
animation: glow-dark 0.75s infinite alternate; animation: glow-dark 0.75s infinite alternate;
@ -671,6 +673,7 @@
} }
} }
} }
}
.application .component.dh-style.card-preview-container { .application .component.dh-style.card-preview-container {
position: relative; position: relative;

View file

@ -3,11 +3,11 @@
.appTheme({ .appTheme({
.item-card-header .item-icons-list .item-icon img { .item-card-header .item-icons-list .item-icon img {
filter: invert(88%) sepia(98%) saturate(1784%) hue-rotate(311deg) brightness(104%) contrast(91%); filter: @golden-filter;
} }
}, { }, {
.item-card-header .item-icons-list .item-icon img { .item-card-header .item-icons-list .item-icon img {
filter: invert(87%) sepia(15%) saturate(343%) hue-rotate(333deg) brightness(110%) contrast(87%); filter: @bright-beige-filter;
} }
}); });

View file

@ -1,8 +1,12 @@
.theme-light .daggerheart.dh-style.setting.homebrew-settings .domains-tab { .theme-light .daggerheart.dh-style.setting.homebrew-settings .domains.tab {
.domains-container .domain-container { .domains-container .domain-container {
.domain-label { .domain-label {
background-image: url('../assets/parchments/dh-parchment-light.png'); background-image: url('../assets/parchments/dh-parchment-light.png');
} }
img {
filter: @dark-filter;
}
} }
} }
@ -11,20 +15,33 @@
.title-wrapper { .title-wrapper {
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center;
justify-content: center; justify-content: center;
gap: 12px;
&:not(:first-child) { &:first-child {
margin-top: 16px; h2 {
margin-top: 0;
}
} }
h4 { .top-lines {
position: relative;
bottom: 8px;
}
h2 {
display: flex;
align-items: center;
font-family: @font-subtitle;
position: relative; position: relative;
width: auto; width: auto;
white-space: nowrap;
margin-top: 16px;
gap: 8px;
color: light-dark(@dark, @beige);
.add-button { .add-button {
position: absolute;
top: 0;
right: -28px;
border-radius: 50%; border-radius: 50%;
width: 24px; width: 24px;
height: 24px; height: 24px;
@ -66,22 +83,12 @@
white-space: nowrap; white-space: nowrap;
text-wrap: auto; text-wrap: auto;
text-align: center; text-align: center;
z-index: 2;
} }
.domain-icon {
&:before {
content: ' ';
position: absolute;
width: 100%;
height: 100%;
z-index: -1;
mask: var(--domain-icon) no-repeat center;
mask-size: contain;
background: linear-gradient(139.01deg, #efe6d8 3.51%, #372e1f 96.49%);
}
}
img { img {
border-radius: 6px; border-radius: 6px;
filter: @beige-filter;
} }
} }
} }
@ -133,6 +140,7 @@
textarea { textarea {
width: 100%; width: 100%;
height: 120px; height: 120px;
margin-top: 4px;
} }
} }
} }

View file

@ -119,6 +119,7 @@
top: -7px; top: -7px;
font-size: 12px; font-size: 12px;
font-variant: petite-caps; font-variant: petite-caps;
z-index: 2;
} }
input { input {

View file

@ -5,6 +5,8 @@
@golden-10: #f3c26710; @golden-10: #f3c26710;
@golden-40: #f3c26740; @golden-40: #f3c26740;
@golden-bg: #f3c2671a; @golden-bg: #f3c2671a;
@golden-filter: brightness(0) saturate(100%) invert(89%) sepia(13%) saturate(2008%) hue-rotate(332deg) brightness(99%)
contrast(92%);
@chat-blue: #8f87ee; @chat-blue: #8f87ee;
@chat-blue-10: #8f87ee10; @chat-blue-10: #8f87ee10;
@ -49,6 +51,7 @@
@dark: #222; @dark: #222;
@dark-15: #22222215; @dark-15: #22222215;
@dark-40: #22222240; @dark-40: #22222240;
@dark-filter: brightness(0) saturate(100%);
@deep-black: #0e0d15; @deep-black: #0e0d15;
@ -56,6 +59,10 @@
@beige-15: #efe6d815; @beige-15: #efe6d815;
@beige-50: #efe6d850; @beige-50: #efe6d850;
@beige-80: #efe6d880; @beige-80: #efe6d880;
@beige-filter: brightness(0) saturate(100%) invert(87%) sepia(25%) saturate(164%) hue-rotate(339deg) brightness(106%)
contrast(87%);
@bright-beige-filter: brightness(0) saturate(100%) invert(87%) sepia(15%) saturate(343%) hue-rotate(333deg)
brightness(110%) contrast(87%);
@soft-white-shadow: rgba(255, 255, 255, 0.05); @soft-white-shadow: rgba(255, 255, 255, 0.05);

View file

@ -4,22 +4,26 @@
data-group="{{tabs.domains.group}}" data-group="{{tabs.domains.group}}"
> >
<div class="title-wrapper"> <div class="title-wrapper">
<h4>{{localize "DAGGERHEART.SETTINGS.Homebrew.domains.domainsTitle"}}</h4> <side-line-div class="invert top-lines"></side-line-div>
<h2>{{localize "DAGGERHEART.SETTINGS.Homebrew.domains.domainsTitle"}}</h2>
<side-line-div class="top-lines"></side-line-div>
</div> </div>
<div class="domains-container"> <div class="domains-container">
{{#each configDomains as | domain |}} {{#each configDomains as | domain |}}
<div class="domain-container"> <div class="domain-container">
<div class="domain-label">{{localize domain.label}}</div> <div class="domain-label">{{localize domain.label}}</div>
<img src="{{domain.src}}" class="domain-icon" style="--domain-icon: url({{domain.src}})" /> <img src="{{domain.src}}" class="domain-icon" />
</div> </div>
{{/each}} {{/each}}
</div> </div>
<div class="title-wrapper"> <div class="title-wrapper">
<h4> <side-line-div class="invert"></side-line-div>
<h2>
{{localize "DAGGERHEART.SETTINGS.Homebrew.domains.homebrewDomains"}} {{localize "DAGGERHEART.SETTINGS.Homebrew.domains.homebrewDomains"}}
<button class="add-button" data-action="addDomain"><i class="fa-solid fa-plus"></i></button> <button class="add-button" data-action="addDomain"><i class="fa-solid fa-plus"></i></button>
</h4> </h2>
<side-line-div></side-line-div>
</div> </div>
<div class="domains-container"> <div class="domains-container">
{{#each homebrewDomains as | domain id |}} {{#each homebrewDomains as | domain id |}}

View file

@ -1,6 +1,6 @@
<section <section
class="tab {{tabs.settings.cssClass}} {{tabs.settings.id}}" class="tab {{tabs.settings.cssClass}} {{tabs.settings.id}}"
data-tab="{{tabs.v.id}}" data-tab="{{tabs.settings.id}}"
data-group="{{tabs.settings.group}}" data-group="{{tabs.settings.group}}"
> >
<div class="scrollable"> <div class="scrollable">

View file

@ -7,8 +7,8 @@
<i class="fa-solid fa-bolt"></i> <i class="fa-solid fa-bolt"></i>
</span> </span>
<span class="item-icon"> <span class="item-icon">
<span class="recall-label">{{localize (concat 'DAGGERHEART.GENERAL.Domain.' source.system.domain '.label')}}</span> <span class="recall-label">{{localize @root.domain.label}}</span>
<img src="{{concat 'systems/daggerheart/assets/icons/domains/' source.system.domain '.svg'}}" alt=""> <img src="{{@root.domain.src}}" alt="">
</span> </span>
</div> </div>
<div class='item-info'> <div class='item-info'>
@ -18,7 +18,7 @@
<h3> <h3>
{{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' source.system.type)}} {{localize (concat 'DAGGERHEART.CONFIG.DomainCardTypes.' source.system.type)}}
<span>-</span> <span>-</span>
{{localize (concat 'DAGGERHEART.GENERAL.Domain.' source.system.domain '.label')}} {{localize @root.domain.label}}
<span>-</span> <span>-</span>
{{localize "DAGGERHEART.GENERAL.level"}}: {{localize "DAGGERHEART.GENERAL.level"}}:
{{source.system.level}} {{source.system.level}}

View file

@ -9,7 +9,7 @@
<span>{{localize "DAGGERHEART.GENERAL.type"}}</span> <span>{{localize "DAGGERHEART.GENERAL.type"}}</span>
{{formField systemFields.type value=source.system.type localize=true}} {{formField systemFields.type value=source.system.type localize=true}}
<span>{{localize "DAGGERHEART.GENERAL.Domain.single"}}</span> <span>{{localize "DAGGERHEART.GENERAL.Domain.single"}}</span>
{{formField systemFields.domain value=source.system.domain localize=true}} {{formField systemFields.domain value=source.system.domain choices=domainChoices valueAttr="id" labelAttr="label" localize=true}}
<span>{{localize "DAGGERHEART.GENERAL.level"}}</span> <span>{{localize "DAGGERHEART.GENERAL.level"}}</span>
{{formField systemFields.level value=source.system.level data-dtype="Number"}} {{formField systemFields.level value=source.system.level data-dtype="Number"}}
<span>{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}</span> <span>{{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}}</span>