diff --git a/src/actor/actor-sheet.ts b/src/actor/actor-sheet.ts index 517af2ee..41ab0088 100644 --- a/src/actor/actor-sheet.ts +++ b/src/actor/actor-sheet.ts @@ -61,12 +61,14 @@ export class DS4ActorSheet extends ActorSheet; enrichedEffects: EnrichedActiveEffectDataSource[]; diff --git a/src/actor/character/character-sheet.ts b/src/actor/character/character-sheet.ts index 04f19ab8..5edf55a0 100644 --- a/src/actor/character/character-sheet.ts +++ b/src/actor/character/character-sheet.ts @@ -2,16 +2,51 @@ // // SPDX-License-Identifier: MIT -import { DS4ActorSheet } from "../actor-sheet"; +import { DS4ActorSheet, DS4ActorSheetData } from "../actor-sheet"; + +import type { DS4Actor } from "../actor"; +import type { Classes, HeroClasses } from "../../apps/class-config"; /** * The Sheet class for DS4 Character Actors */ -export class DS4CharacterActorSheet extends DS4ActorSheet { +export class DS4CharacterSheet extends DS4ActorSheet { /** @override */ static get defaultOptions(): ActorSheet.Options { return foundry.utils.mergeObject(super.defaultOptions, { classes: ["sheet", "ds4-actor-sheet", "ds4-character-sheet"], }); } + + /** @override */ + async getData(): Promise { + const data = await super.getData(); + const settings = data.settings; + + const classes: Classes = { + none: { name: "DS4.ClassNone", hasSpellCasterConfiguration: false }, + ...settings.classes, + }; + const heroClasses: HeroClasses = Object.fromEntries([ + ["none", { name: "DS4.HeroClassNone", baseClass: "none", hasSpellCasterConfiguration: false }], + ...Object.entries(settings.heroClasses).filter( + ([, value]) => this.actor.data.data.baseInfo.class === value.baseClass, + ), + ]); + + return { + ...data, + classes, + heroClasses, + }; + } +} + +export interface DS4CharacterSheet { + object: DS4Actor & { data: { type: "character"; _source: { type: "character" } } }; +} + +interface DS4CharacterSheetData extends DS4ActorSheetData { + classes: Classes; + heroClasses: HeroClasses; } diff --git a/src/actor/creature/creature-sheet.ts b/src/actor/creature/creature-sheet.ts index 94aa940d..e42c5369 100644 --- a/src/actor/creature/creature-sheet.ts +++ b/src/actor/creature/creature-sheet.ts @@ -7,7 +7,7 @@ import { DS4ActorSheet } from "../actor-sheet"; /** * The Sheet class for DS4 Creature Actors */ -export class DS4CreatureActorSheet extends DS4ActorSheet { +export class DS4CreatureSheet extends DS4ActorSheet { /** @override */ static get defaultOptions(): ActorSheet.Options { return foundry.utils.mergeObject(super.defaultOptions, { diff --git a/src/apps/class-config.ts b/src/apps/class-config.ts new file mode 100644 index 00000000..fc4b6de8 --- /dev/null +++ b/src/apps/class-config.ts @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2022 Johannes Loher +// +// SPDX-License-Identifier: MIT + +import { DS4 } from "../config"; +import { getGame } from "../helpers"; + +export class ClassConfig extends FormApplication { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + template: "systems/ds4/templates/apps/class-config.hbs", + }); + } + + static get defaultClasses(): Classes { + return Object.fromEntries( + Object.entries(DS4.defaultClasses).map(([key, { i18nKey: name, hasSpellCasterConfiguration }]) => [ + key, + { name, hasSpellCasterConfiguration }, + ]), + ); + } + + static get defaultHeroClasses(): HeroClasses { + return Object.fromEntries( + Object.entries(DS4.defaultHeroClasses).map( + ([key, { i18nKey: name, baseClass, hasSpellCasterConfiguration }]) => [ + key, + { name, baseClass, hasSpellCasterConfiguration }, + ], + ), + ); + } + + /** @override */ + getData(): ClassConfigData { + const game = getGame(); + return { + classes: game.settings.get("ds4", "classes"), + heroClasses: game.settings.get("ds4", "heroClasses"), + }; + } + + protected _updateObject(event: Event, formData?: object): Promise { + throw new Error("Method not implemented."); + } +} + +interface Class { + name: string; + hasSpellCasterConfiguration: boolean; +} + +export type Classes = Record; + +interface HeroClass { + name: string; + baseClass: string; + hasSpellCasterConfiguration: boolean; +} + +export type HeroClasses = Record; + +interface ClassConfigData { + classes: Classes; + heroClasses: HeroClasses; +} diff --git a/src/config.ts b/src/config.ts index 731744d8..76c206ca 100644 --- a/src/config.ts +++ b/src/config.ts @@ -426,4 +426,105 @@ export const DS4 = { eyeColor: "String", specialCharacteristics: "String", }, + + defaultClasses: { + fighter: { + i18nKey: "DS4.ClassFighter", + hasSpellCasterConfiguration: false, + }, + scout: { + i18nKey: "DS4.ClassScout", + hasSpellCasterConfiguration: false, + }, + healer: { + i18nKey: "DS4.ClassHealer", + hasSpellCasterConfiguration: true, + }, + wizard: { + i18nKey: "DS4.ClassWizard", + hasSpellCasterConfiguration: true, + }, + sorcerer: { + i18nKey: "DS4.ClassSorcerer", + hasSpellCasterConfiguration: true, + }, + }, + + defaultHeroClasses: { + berserker: { + i18nKey: "DS4.HeroClassBerserker", + baseClass: "fighter", + hasSpellCasterConfiguration: false, + }, + weaponMaster: { + i18nKey: "DS4.HeroClassWeaponMaster", + baseClass: "fighter", + hasSpellCasterConfiguration: false, + }, + paladin: { + i18nKey: "DS4.HeroClassPaladin", + baseClass: "fighter", + hasSpellCasterConfiguration: true, + }, + assassin: { + i18nKey: "DS4.HeroClassAssassin", + baseClass: "scout", + hasSpellCasterConfiguration: false, + }, + ranger: { + i18nKey: "DS4.HeroClassRanger", + baseClass: "scout", + hasSpellCasterConfiguration: false, + }, + rogue: { + i18nKey: "DS4.HeroClassRogue", + baseClass: "scout", + hasSpellCasterConfiguration: false, + }, + cleric: { + i18nKey: "DS4.HeroClassCleric", + baseClass: "healer", + hasSpellCasterConfiguration: false, + }, + druid: { + i18nKey: "DS4.HeroClassDruid", + baseClass: "healer", + hasSpellCasterConfiguration: false, + }, + monk: { + i18nKey: "DS4.HeroClassMonk", + baseClass: "healer", + hasSpellCasterConfiguration: false, + }, + archmage: { + i18nKey: "DS4.HeroClassArchmage", + baseClass: "wizard", + hasSpellCasterConfiguration: false, + }, + elementalist: { + i18nKey: "DS4.HeroClassElementalist", + baseClass: "wizard", + hasSpellCasterConfiguration: false, + }, + battleMage: { + i18nKey: "DS4.HeroClassBattleMage", + baseClass: "wizard", + hasSpellCasterConfiguration: false, + }, + bloodMage: { + i18nKey: "DS4.HeroClassBloodMage", + baseClass: "sorcerer", + hasSpellCasterConfiguration: false, + }, + demonologist: { + i18nKey: "DS4.HeroClassDemonologist", + baseClass: "sorcerer", + hasSpellCasterConfiguration: false, + }, + necromancer: { + i18nKey: "DS4.HeroClassNecromancer", + baseClass: "sorcerer", + hasSpellCasterConfiguration: false, + }, + }, }; diff --git a/src/global.d.ts b/src/global.d.ts index 91fc6c12..e557d128 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -2,12 +2,16 @@ // // SPDX-License-Identifier: MIT +import type { Classes, HeroClasses } from "./apps/class-config"; + declare global { namespace ClientSettings { interface Values { "ds4.systemMigrationVersion": number; "ds4.useSlayingDiceForAutomatedChecks": boolean; "ds4.showSlayerPoints": boolean; + "ds4.classes": Classes; + "ds4.heroClasses": HeroClasses; } } diff --git a/src/hooks/init.ts b/src/hooks/init.ts index ee2f8870..0aa55ef3 100644 --- a/src/hooks/init.ts +++ b/src/hooks/init.ts @@ -5,8 +5,8 @@ // SPDX-License-Identifier: MIT import { DS4ActiveEffect } from "../active-effect"; -import { DS4CharacterActorSheet } from "../actor/character/character-sheet"; -import { DS4CreatureActorSheet } from "../actor/creature/creature-sheet"; +import { DS4CharacterSheet } from "../actor/character/character-sheet"; +import { DS4CreatureSheet } from "../actor/creature/creature-sheet"; import { DS4ActorProxy } from "../actor/proxy"; import { DS4ChatMessage } from "../chat-message"; import { DS4 } from "../config"; @@ -66,8 +66,8 @@ async function init() { registerSystemSettings(); Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true }); - Actors.registerSheet("ds4", DS4CreatureActorSheet, { types: ["creature"], makeDefault: true }); + Actors.registerSheet("ds4", DS4CharacterSheet, { types: ["character"], makeDefault: true }); + Actors.registerSheet("ds4", DS4CreatureSheet, { types: ["creature"], makeDefault: true }); Items.unregisterSheet("core", ItemSheet); Items.registerSheet("ds4", DS4ItemSheet, { makeDefault: true }); diff --git a/src/settings.ts b/src/settings.ts index a2d1017e..bf52e439 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: MIT +import { ClassConfig, Classes, HeroClasses } from "./apps/class-config"; import { getGame } from "./helpers"; export function registerSystemSettings(): void { @@ -11,7 +12,6 @@ export function registerSystemSettings(): void { * Track the migrations version of the latest migration that has been applied */ game.settings.register("ds4", "systemMigrationVersion", { - name: "System Migration Version", scope: "world", config: false, type: Number, @@ -35,12 +35,37 @@ export function registerSystemSettings(): void { type: Boolean, default: false, }); + + game.settings.register("ds4", "classes", { + scope: "world", + config: false, + default: ClassConfig.defaultClasses, + onChange: renderActorAndSpellSheets, + }); + + game.settings.register("ds4", "heroClasses", { + scope: "world", + config: false, + default: ClassConfig.defaultHeroClasses, + onChange: renderActorAndSpellSheets, + }); + + game.settings.registerMenu("ds4", "classes", { + name: "DS4.MenuClassesName", + hint: "DS4.MenuClassesHint", + label: "DS4.MenuClassesLabel", + icon: "fas fa-chess", + restricted: true, + type: ClassConfig, + }); } export interface DS4Settings { systemMigrationVersion: number; useSlayingDiceForAutomatedChecks: boolean; showSlayerPoints: boolean; + classes: Classes; + heroClasses: HeroClasses; } export function getDS4Settings(): DS4Settings { @@ -49,5 +74,18 @@ export function getDS4Settings(): DS4Settings { systemMigrationVersion: game.settings.get("ds4", "systemMigrationVersion"), useSlayingDiceForAutomatedChecks: game.settings.get("ds4", "useSlayingDiceForAutomatedChecks"), showSlayerPoints: game.settings.get("ds4", "showSlayerPoints"), + classes: game.settings.get("ds4", "classes"), + heroClasses: game.settings.get("ds4", "heroClasses"), }; } + +function renderActorAndSpellSheets() { + for (const application of Object.values(ui.windows)) { + if ( + application instanceof ActorSheet || + (application instanceof ItemSheet && application.item.type === "spell") + ) { + application.render(false); + } + } +} diff --git a/template.json b/template.json index d4ec6bc3..64fac089 100644 --- a/template.json +++ b/template.json @@ -87,8 +87,8 @@ "templates": ["base"], "baseInfo": { "race": "", - "class": "", - "heroClass": "", + "class": "none", + "heroClass": "none", "culture": "" }, "progression": { diff --git a/templates/apps/class-config.hbs b/templates/apps/class-config.hbs new file mode 100644 index 00000000..82846e66 --- /dev/null +++ b/templates/apps/class-config.hbs @@ -0,0 +1,19 @@ +{{!-- +SPDX-FileCopyrightText: 2022 Johannes Loher + +SPDX-License-Identifier: MIT +--}} + +
+
    + {{#each classes as | value key |}} +
  • {{localize value.name}}
  • + {{/each}} +
+
+
    + {{#each heroClasses as | value key |}} +
  • {{localize value.name}}
  • + {{/each}} +
+
diff --git a/templates/sheets/actor/components/character-properties.hbs b/templates/sheets/actor/components/character-properties.hbs index 6d337395..b408941c 100644 --- a/templates/sheets/actor/components/character-properties.hbs +++ b/templates/sheets/actor/components/character-properties.hbs @@ -51,13 +51,23 @@ SPDX-License-Identifier: MIT
- +
- +