diff --git a/lang/de.json b/lang/de.json index 2c0dfd5f..325af8e7 100644 --- a/lang/de.json +++ b/lang/de.json @@ -209,6 +209,28 @@ "DS4.CharacterProgressionProgressPoints": "Lernpunkte", "DS4.CharacterSlayerPoints": "Slayerpunkte", "DS4.CharacterSlayerPointsAbbr": "SP", + "DS4.ClassNone": "Keine", + "DS4.ClassFighter": "Krieger", + "DS4.ClassScout": "Späher", + "DS4.ClassHealer": "Heiler", + "DS4.ClassWizard": "Zauberer", + "DS4.ClassSorcerer": "Schwarzmagier", + "DS4.HeroClassNone": "Keine", + "DS4.HeroClassBerserker": "Berserker", + "DS4.HeroClassWeaponMaster": "Waffenmeister", + "DS4.HeroClassPaladin": "Paladin", + "DS4.HeroClassAssassin": "Attentäter", + "DS4.HeroClassRanger": "Waldläufer", + "DS4.HeroClassRogue": "Meisterdieb", + "DS4.HeroClassCleric": "Kleriker", + "DS4.HeroClassDruid": "Druide", + "DS4.HeroClassMonk": "Kampfmönch", + "DS4.HeroClassArchmage": "Erzmagier", + "DS4.HeroClassElementalist": "Elementarist", + "DS4.HeroClassBattleMage": "Kriegszauberer", + "DS4.HeroClassBloodMage": "Blutmagier", + "DS4.HeroClassDemonologist": "Dämonologe", + "DS4.HeroClassNecromancer": "Nekromant", "DS4.TalentRank": "Rang", "DS4.SortByTalentRank": "Nach Rang sortieren", "DS4.TalentRankBase": "Erworben", @@ -314,6 +336,9 @@ "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Benutze Slayende Würfel bei automatisierten Proben.", "DS4.SettingShowSlayerPointsName": "Slayerpunkte", "DS4.SettingShowSlayerPointsHint": "Zeige Slayerpunkte im Charakterbogen an.", + "DS4.MenuClassesName": "Klassenkonfiguration", + "DS4.MenuClassesHint": "Konfiguriere welche Klassen und Heldenklassen verwendet werden können.", + "DS4.MenuClassesLabel": "Klassen Konfigurieren", "DS4.Checks": "Proben", "DS4.ChecksAppraise": "Schätzen", "DS4.ChecksChangeSpell": "Zauber Wechseln", diff --git a/lang/en.json b/lang/en.json index 19973936..1393881d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -209,6 +209,28 @@ "DS4.CharacterProgressionProgressPoints": "Progress Points", "DS4.CharacterSlayerPoints": "Slayer Points", "DS4.CharacterSlayerPointsAbbr": "SP", + "DS4.ClassNone": "None", + "DS4.ClassFighter": "Fighter", + "DS4.ClassScout": "Scout", + "DS4.ClassHealer": "Healer", + "DS4.ClassWizard": "Wizard", + "DS4.ClassSorcerer": "Sorcerer", + "DS4.HeroClassNone": "None", + "DS4.HeroClassBerserker": "Berserker", + "DS4.HeroClassWeaponMaster": "Weapon Master", + "DS4.HeroClassPaladin": "Paladin", + "DS4.HeroClassAssassin": "Assasin", + "DS4.HeroClassRanger": "Ranger", + "DS4.HeroClassRogue": "Rogue", + "DS4.HeroClassCleric": "Cleric", + "DS4.HeroClassDruid": "Druid", + "DS4.HeroClassMonk": "Monk", + "DS4.HeroClassArchmage": "Archmage", + "DS4.HeroClassElementalist": "Elementalist", + "DS4.HeroClassBattleMage": "Battle Mage", + "DS4.HeroClassBloodMage": "Blood Mage", + "DS4.HeroClassDemonologist": "Demonologist", + "DS4.HeroClassNecromancer": "Necromancer", "DS4.TalentRank": "Rank", "DS4.SortByTalentRank": "Sort by Rank", "DS4.TalentRankBase": "Acquired", @@ -314,6 +336,9 @@ "DS4.SettingUseSlayingDiceForAutomatedChecksHint": "Use Slaying Dice for automated checks.", "DS4.SettingShowSlayerPointsName": "Slayer Points", "DS4.SettingShowSlayerPointsHint": "Show Slayer Points in the character sheet.", + "DS4.MenuClassesName": "Class Configuration", + "DS4.MenuClassesHint": "Configure which classes and hero classes can be used.", + "DS4.MenuClassesLabel": "Configure Classes", "DS4.Checks": "Checks", "DS4.ChecksAppraise": "Appraise", "DS4.ChecksChangeSpell": "Change Spell", diff --git a/rollup.config.js b/rollup.config.js index 98331152..29c08361 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -60,6 +60,7 @@ const config = { rename: (name, extension) => `${name.replace(".json", ".db")}.${extension}`, }, ], + verbose: true, }), isProduction && terser({ ecma: 2020, keep_fnames: true }), ], diff --git a/scss/components/actor/_actor_header.scss b/scss/components/actor/_actor_header.scss index 075fc3ed..a6433367 100644 --- a/scss/components/actor/_actor_header.scss +++ b/scss/components/actor/_actor_header.scss @@ -15,8 +15,8 @@ &__img { border: none; cursor: pointer; - height: 100px; - width: 100px; + height: 125px; + width: 125px; } &__data { diff --git a/scss/components/actor/_actor_properties.scss b/scss/components/actor/_actor_properties.scss index 36e473f6..74570d23 100644 --- a/scss/components/actor/_actor_properties.scss +++ b/scss/components/actor/_actor_properties.scss @@ -11,11 +11,17 @@ .ds4-actor-properties { @include mixins.mark-invalid-or-disabled-input; - display: flex; - gap: 0.25em; + column-gap: 0.25em; + display: grid; + grid-template-columns: repeat(4, 1fr); &__property { - flex: 1; + text-align: center; + width: 100%; + + &--wide { + grid-column: 1 / -1; + } } &__property-label { diff --git a/scss/components/actor/_actor_progression.scss b/scss/components/actor/_actor_resources.scss similarity index 97% rename from scss/components/actor/_actor_progression.scss rename to scss/components/actor/_actor_resources.scss index 09a3a185..310da566 100644 --- a/scss/components/actor/_actor_progression.scss +++ b/scss/components/actor/_actor_resources.scss @@ -9,7 +9,7 @@ @use "../../utils/mixins"; @use "../../utils/variables"; -.ds4-actor-progression { +.ds4-actor-resources { @include mixins.mark-invalid-or-disabled-input; display: flex; gap: 0.5em; diff --git a/scss/components/actor/_actor_sheet.scss b/scss/components/actor/_actor_sheet.scss index a49d1934..f33efc55 100644 --- a/scss/components/actor/_actor_sheet.scss +++ b/scss/components/actor/_actor_sheet.scss @@ -5,6 +5,6 @@ */ .ds4-actor-sheet { - min-height: 625px; + min-height: 655px; min-width: 650px; } diff --git a/scss/ds4.scss b/scss/ds4.scss index acedd839..ef2d2995 100644 --- a/scss/ds4.scss +++ b/scss/ds4.scss @@ -23,8 +23,8 @@ // actor @use "components/actor/actor_header"; -@use "components/actor/actor_progression"; @use "components/actor/actor_properties"; +@use "components/actor/actor_resources"; @use "components/actor/actor_sheet"; @use "components/actor/biography"; @use "components/actor/check"; diff --git a/src/actor/actor-sheet.ts b/src/actor/actor-sheet.ts index 90e910fa..7f2919d4 100644 --- a/src/actor/actor-sheet.ts +++ b/src/actor/actor-sheet.ts @@ -25,7 +25,7 @@ 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/handlebars/handlebars-partials.ts b/src/handlebars/handlebars-partials.ts index 0a0ca83e..2a22cdb5 100644 --- a/src/handlebars/handlebars-partials.ts +++ b/src/handlebars/handlebars-partials.ts @@ -7,7 +7,7 @@ export default async function registerHandlebarsPartials(): Promise { const templatePaths = [ "systems/ds4/templates/sheets/actor/components/actor-header.hbs", - "systems/ds4/templates/sheets/actor/components/actor-progression.hbs", + "systems/ds4/templates/sheets/actor/components/actor-resources.hbs", "systems/ds4/templates/sheets/actor/components/biography.hbs", "systems/ds4/templates/sheets/actor/components/character-properties.hbs", "systems/ds4/templates/sheets/actor/components/check.hbs", 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..2ed87da1 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: rerenderApplications(isActorSheet, isSpellSheet), + }); + + game.settings.register("ds4", "heroClasses", { + scope: "world", + config: false, + default: ClassConfig.defaultHeroClasses, + onChange: rerenderApplications(isActorSheet, isSpellSheet), + }); + + 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,24 @@ 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 rerenderApplications(...conditions: ((app: Application) => boolean)[]): () => void { + return () => { + outer: for (const app of Object.values(ui.windows)) { + for (const condition of conditions) { + if (condition(app)) { + app.render(false); + break outer; + } + } + app.render(false); + } + }; +} + +const isActorSheet = (app: Application): boolean => app instanceof ActorSheet; +const isSpellSheet = (app: Application): boolean => app instanceof ItemSheet && app.item.type === "spell"; 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/actor-header.hbs b/templates/sheets/actor/components/actor-header.hbs index dba65aba..dbd88164 100644 --- a/templates/sheets/actor/components/actor-header.hbs +++ b/templates/sheets/actor/components/actor-header.hbs @@ -20,8 +20,9 @@ SPDX-License-Identifier: MIT - {{> systems/ds4/templates/sheets/actor/components/actor-progression.hbs}} + {{> systems/ds4/templates/sheets/actor/components/actor-resources.hbs}} +
{{> @partial-block}}
diff --git a/templates/sheets/actor/components/actor-progression.hbs b/templates/sheets/actor/components/actor-progression.hbs deleted file mode 100644 index bfb3a860..00000000 --- a/templates/sheets/actor/components/actor-progression.hbs +++ /dev/null @@ -1,48 +0,0 @@ -{{!-- -SPDX-FileCopyrightText: 2021 Johannes Loher -SPDX-FileCopyrightText: 2021 Oliver Rümpelein -SPDX-FileCopyrightText: 2021 Gesina Schwalbe - -SPDX-License-Identifier: MIT ---}} - -
-
-

-

- -
- {{#if (eq data.type "character")}} - {{#if settings.showSlayerPoints}} -
-

-

- -
- {{/if}} -
-

-

- -
-
-

-

- -
- {{/if}} -
diff --git a/templates/sheets/actor/components/actor-resources.hbs b/templates/sheets/actor/components/actor-resources.hbs new file mode 100644 index 00000000..b0b275d1 --- /dev/null +++ b/templates/sheets/actor/components/actor-resources.hbs @@ -0,0 +1,29 @@ +{{!-- +SPDX-FileCopyrightText: 2021 Johannes Loher +SPDX-FileCopyrightText: 2021 Oliver Rümpelein +SPDX-FileCopyrightText: 2021 Gesina Schwalbe + +SPDX-License-Identifier: MIT +--}} + +
+
+

+

+ +
+ {{#if (and (eq data.type "character") settings.showSlayerPoints)}} +
+

+

+ +
+ {{/if}} +
diff --git a/templates/sheets/actor/components/character-properties.hbs b/templates/sheets/actor/components/character-properties.hbs index 6d337395..264924cf 100644 --- a/templates/sheets/actor/components/character-properties.hbs +++ b/templates/sheets/actor/components/character-properties.hbs @@ -9,15 +9,15 @@ SPDX-License-Identifier: MIT
- + for="data.progression.level-{{data._id}}">{{config.i18n.characterProgression.level}} +
- + for="data.progression.experiencePoints-{{data._id}}">{{config.i18n.characterProgression.experiencePoints}} +
+
+ + +
+
+ + +
- +
- +
diff --git a/templates/sheets/actor/components/creature-properties.hbs b/templates/sheets/actor/components/creature-properties.hbs index a0d99f96..32594e8a 100644 --- a/templates/sheets/actor/components/creature-properties.hbs +++ b/templates/sheets/actor/components/creature-properties.hbs @@ -19,18 +19,6 @@ SPDX-License-Identifier: MIT {{/select}} -
- - -
-
- - -
@@ -43,10 +31,22 @@ SPDX-License-Identifier: MIT {{/select}}
+
+ + +
+
+ + +