Fanewick was once a place of great abundance and peace
+ dangerous to those unfamiliar with the land, but a cornucopia
+ to those who respected its ways. When Haven invaded the
+ wilds and forced the land into eternal spring, a dangerous
+ bloom known as the Witherwild took hold and now threatens
+ the lives of all who live there. In a Witherwild campaign,
+ you’ll play unlikely heroes from humble beginnings who are
+ reckoning with their newfound duty to save Fanewick’s people
+ from dangerous corruption.
`,
+ toneAndFeel: 'Adventurous, Dynamic, Epic, Heroic, Thrilling, Uncanny, Whimsical',
+ themes: 'Cultural Clash, Ends Justify Means, Grief, People vs. Nature, Transformation and Change, Survival',
+ touchstones: 'Princess Mononoke, The Legend of Zelda, The Dark Crystal, Nausicaä of the Valley of the Wind'
+ }
+ });
+ /* Temporary for testing */
+
runMigrations();
});
diff --git a/lang/en.json b/lang/en.json
index 0a9448c4..ecd7ed70 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -354,6 +354,9 @@
"Attribution": {
"title": "Attribution"
},
+ "CampaignFrames": {
+ "title": "Campaign Frames"
+ },
"CharacterCreation": {
"tabs": {
"ancestry": "Ancestry",
diff --git a/module/applications/_module.mjs b/module/applications/_module.mjs
index b2d3001a..796edd20 100644
--- a/module/applications/_module.mjs
+++ b/module/applications/_module.mjs
@@ -1,3 +1,4 @@
+export * as campaignFrame from './campaignFrame/_module.mjs';
export * as characterCreation from './characterCreation/_module.mjs';
export * as dialogs from './dialogs/_module.mjs';
export * as hud from './hud/_module.mjs';
diff --git a/module/applications/campaignFrame/_module.mjs b/module/applications/campaignFrame/_module.mjs
new file mode 100644
index 00000000..e151e679
--- /dev/null
+++ b/module/applications/campaignFrame/_module.mjs
@@ -0,0 +1 @@
+export { default as CampaignFrames } from './campaignFrames.mjs';
diff --git a/module/applications/campaignFrame/campaignFrames.mjs b/module/applications/campaignFrame/campaignFrames.mjs
new file mode 100644
index 00000000..c70fac28
--- /dev/null
+++ b/module/applications/campaignFrame/campaignFrames.mjs
@@ -0,0 +1,46 @@
+const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
+export default class DhCampaignFrames extends HandlebarsApplicationMixin(ApplicationV2) {
+ constructor() {
+ super({});
+ }
+
+ get title() {
+ return game.i18n.format('DAGGERHEART.APPLICATIONS.CampaignFrames.title');
+ }
+
+ static DEFAULT_OPTIONS = {
+ classes: ['daggerheart', 'dh-style', 'campaign-frame'],
+ position: { width: 640, height: 'auto' },
+ window: { icon: 'fa-solid fa-globe' },
+ actions: {}
+ };
+
+ static PARTS = {
+ application: {
+ id: 'campaign-frames',
+ template: 'systems/daggerheart/templates/campaignFrames/campaign-frames.hbs'
+ }
+ };
+
+ getCampaignFrameTabs(frames) {
+ for (const v of Object.values(frames)) {
+ v.active = this.tabGroups[v.group]
+ ? this.tabGroups[v.group] === v.id
+ : this.tabGroups.primary !== 'equipment'
+ ? v.active
+ : false;
+ v.cssClass = v.active ? 'active' : '';
+ }
+
+ return tabs;
+ }
+
+ async _prepareContext(_options) {
+ const context = await super._prepareContext(_options);
+
+ context.campaignFrames = game.system.campaignFrames.frames;
+ // context.campaignFrameTabs = this.getCampaignFrameTabs(context.campaignFrames);
+
+ return context;
+ }
+}
diff --git a/module/applications/sidebar/tabs/daggerheartMenu.mjs b/module/applications/sidebar/tabs/daggerheartMenu.mjs
index 86c1b8cb..5e1ec9a5 100644
--- a/module/applications/sidebar/tabs/daggerheartMenu.mjs
+++ b/module/applications/sidebar/tabs/daggerheartMenu.mjs
@@ -32,7 +32,8 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
actions: {
selectRefreshable: DaggerheartMenu.#selectRefreshable,
refreshActors: DaggerheartMenu.#refreshActors,
- createFallCollisionDamage: DaggerheartMenu.#createFallCollisionDamage
+ createFallCollisionDamage: DaggerheartMenu.#createFallCollisionDamage,
+ openCampaignFrames: DaggerheartMenu.#openCampaignFrames
}
};
@@ -91,4 +92,8 @@ export default class DaggerheartMenu extends HandlebarsApplicationMixin(Abstract
sound: CONFIG.sounds.dice
});
}
+
+ static async #openCampaignFrames() {
+ new game.system.api.applications.campaignFrame.CampaignFrames().render({ force: true });
+ }
}
diff --git a/module/data/_module.mjs b/module/data/_module.mjs
index a54da0e1..028b3c8e 100644
--- a/module/data/_module.mjs
+++ b/module/data/_module.mjs
@@ -1,3 +1,4 @@
+export { default as CampaignFrames } from './campaignFrames.mjs';
export { default as DhCombat } from './combat.mjs';
export { default as DhCombatant } from './combatant.mjs';
export { default as DhRollTable } from './rollTable.mjs';
diff --git a/module/data/campaignFrames.mjs b/module/data/campaignFrames.mjs
new file mode 100644
index 00000000..0a3a4150
--- /dev/null
+++ b/module/data/campaignFrames.mjs
@@ -0,0 +1,29 @@
+export default class DhCampaignFrames extends foundry.abstract.TypeDataModel {
+ static defineSchema() {
+ const fields = foundry.data.fields;
+
+ return {
+ frames: new fields.TypedObjectField(new fields.EmbeddedDataField(DhCampaignFrame))
+ };
+ }
+
+ register(frames) {
+ this.updateSource({ frames });
+ }
+}
+
+class DhCampaignFrame extends foundry.abstract.DataModel {
+ static defineSchema() {
+ const fields = foundry.data.fields;
+
+ return {
+ name: new fields.StringField({ required: true }),
+ img: new fields.FilePathField({ initial: 'icons/svg/mountain.svg', categories: ['IMAGE'], base64: false }),
+ complexityRating: new fields.NumberField({ required: true, integer: true }),
+ pitch: new fields.HTMLField(),
+ toneAndFeel: new fields.StringField(),
+ themes: new fields.StringField(),
+ touchstones: new fields.StringField()
+ };
+ }
+}
diff --git a/styles/less/dialog/campaign-frame/sheet.less b/styles/less/dialog/campaign-frame/sheet.less
new file mode 100644
index 00000000..d7264a58
--- /dev/null
+++ b/styles/less/dialog/campaign-frame/sheet.less
@@ -0,0 +1,32 @@
+.application.daggerheart.dh-style.campaign-frame {
+ .campaign-frames-container {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ .campaign-frame-container {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+
+ .information-container {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+
+ label {
+ font-size: var(--font-size-20);
+ }
+
+ .subtext {
+ color: var(--beige-50);
+ font-style: italic;
+ }
+
+ .italic {
+ font-style: italic;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/styles/less/dialog/index.less b/styles/less/dialog/index.less
index 11d9635e..183ea01a 100644
--- a/styles/less/dialog/index.less
+++ b/styles/less/dialog/index.less
@@ -42,3 +42,5 @@
@import './character-reset/sheet.less';
@import './compendiumBrowserPackDialog/sheet.less';
+
+@import './campaign-frame/sheet.less';
\ No newline at end of file
diff --git a/templates/campaignFrames/campaign-frames.hbs b/templates/campaignFrames/campaign-frames.hbs
new file mode 100644
index 00000000..f81d5dc2
--- /dev/null
+++ b/templates/campaignFrames/campaign-frames.hbs
@@ -0,0 +1,41 @@
+