From ecfbe9fa2a6a52697140194ad1bff478eab61739 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 19 Jan 2021 03:31:40 +0100 Subject: [PATCH 1/3] automatically calculate base combat values --- src/lang/de.json | 3 + src/lang/en.json | 3 + src/module/actor/actor.ts | 49 ++++++++++-- src/module/actor/sheets/actor-sheet.ts | 1 + src/module/common/common-data.ts | 8 +- src/module/ds4.ts | 9 +++ src/module/item/item-data.ts | 4 +- src/module/item/item-sheet.ts | 1 + src/module/migrations.ts | 77 +++++++++++++++++++ src/module/migrations/0-1-0.ts | 28 +++++++ src/module/rolls/check-factory.ts | 6 +- src/module/settings.ts | 12 +++ src/scss/components/_combat_values.scss | 5 +- src/template.json | 8 -- .../actor/partials/combat-values.hbs | 8 +- 15 files changed, 195 insertions(+), 27 deletions(-) create mode 100644 src/module/migrations.ts create mode 100644 src/module/migrations/0-1-0.ts create mode 100644 src/module/settings.ts diff --git a/src/lang/de.json b/src/lang/de.json index 59146324..fcb1a3d9 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -175,6 +175,9 @@ "DS4.WarningActorCannotOwnItem": "Der Aktor '{actorName}' vom Typ '{actorType}' kann das Item '{itemName}' vom Typ '{itemType}' nicht besitzen.", "DS4.ErrorDiceCritOverlap": "Es gibt eine Überlappung zwischen Patzern und Immersiegen.", "DS4.ErrorExplodingRecursionLimitExceeded": "Die maximale Rekursionstiefe für slayende Würfelwürfe wurde überschritten.", + "DS4.ErrorDuringMigration": "Fehler während der Aktualisierung des DS4 Systems von Version {currentVersion} auf {targetVersion}. Der Fehler trat während der Ausführung des Migrationsskripts mit der Version {migrationVersion} auf. Spätere Migrationsskripte wurden nicht ausgeführt. Mehr Details finden Sie in der Entwicklerkonsole (F12).", + "DS4.InfoSystemUpdateStart": "Aktualisiere DS4 System von Version {currentVersion} auf {targetVersion}. Bitte haben Sie etwas Geduld, schließen Sie nicht das Spiel und fahren Sie nicht den Server herunter.", + "DS4.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Version {currentVersion} auf {targetVersion} erfolgreich!", "DS4.UnitRounds": "Runden", "DS4.UnitRoundsAbbr": "Rnd", "DS4.UnitMinutes": "Minuten", diff --git a/src/lang/en.json b/src/lang/en.json index c28b165f..fe2c2855 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -175,6 +175,9 @@ "DS4.WarningActorCannotOwnItem": "The actor '{actorName}' of type '{actorType}' cannot own the item '{itemName}' of type '{itemType}'.", "DS4.ErrorDiceCritOverlap": "There's an overlap between Fumbles and Coups", "DS4.ErrorExplodingRecursionLimitExceeded": "Maximum recursion depth for exploding dice roll exceeded", + "DS4.ErrorDuringMigration": "Error while migrating DS4 system from version {currentVersion} to {targetVersion}. The error occurred during execution of migration script with version {migrationVersion}. Later migrations have not been executed. For more details, please look at the development console (F12).", + "DS4.InfoSystemUpdateStart": "Migrating DS4 system from version {currentVersion} to {targetVersion}. Please be patient and do not close your game or shut down your server.", + "DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from version {currentVersion} to {targetVersion} successful!", "DS4.UnitRounds": "Rounds", "DS4.UnitRoundsAbbr": "rnd", "DS4.UnitMinutes": "Minutes", diff --git a/src/module/actor/actor.ts b/src/module/actor/actor.ts index df3ad6d7..0ad31fb8 100644 --- a/src/module/actor/actor.ts +++ b/src/module/actor/actor.ts @@ -1,6 +1,6 @@ import { ModifiableData } from "../common/common-data"; import { DS4Item } from "../item/item"; -import { DS4ItemDataType, ItemType } from "../item/item-data"; +import { DS4Armor, DS4ItemDataType, DS4Shield, ItemType } from "../item/item-data"; import { DS4ActorDataType } from "./actor-data"; export class DS4Actor extends Actor { @@ -15,12 +15,7 @@ export class DS4Actor extends Actor const traits = data.data.traits; Object.values(traits).forEach((trait: ModifiableData) => (trait.total = trait.base + trait.mod)); - const combatValues = data.data.combatValues; - Object.values(combatValues).forEach( - (combatValue: ModifiableData) => (combatValue.total = combatValue.base + combatValue.mod), - ); - - combatValues.hitPoints.max = combatValues.hitPoints.total; + this._prepareCombatValues(); } /** @@ -55,4 +50,44 @@ export class DS4Actor extends Actor canOwnItemType(itemType: ItemType): boolean { return this.ownableItemTypes.includes(itemType); } + + /** + * Prepares the combat values of the actor. + */ + private _prepareCombatValues(): void { + const data = this.data.data; + const armorValueOfEquippedItems = this._calculateArmorValueOfEquippedItems(); + data.combatValues.hitPoints.base = + (data.attributes.body.total ?? 0) + (data.traits.constitution.total ?? 0) + 10; + + data.combatValues.defense.base = + (data.attributes.body.total ?? 0) + (data.traits.constitution.total ?? 0) + armorValueOfEquippedItems; + data.combatValues.initiative.base = (data.attributes.mobility.total ?? 0) + (data.traits.agility.total ?? 0); + data.combatValues.movement.base = (data.attributes.mobility.total ?? 0) / 2 + 1; + data.combatValues.meleeAttack.base = (data.attributes.body.total ?? 0) + (data.traits.strength.total ?? 0); + data.combatValues.rangedAttack.base = + (data.attributes.mobility.total ?? 0) + (data.traits.dexterity.total ?? 0); + data.combatValues.spellcasting.base = + (data.attributes.mind.total ?? 0) + (data.traits.aura.total ?? 0) - armorValueOfEquippedItems; + data.combatValues.targetedSpellcasting.base = + (data.attributes.mind.total ?? 0) + (data.traits.dexterity.total ?? 0) - armorValueOfEquippedItems; + + Object.values(data.combatValues).forEach( + (combatValue: ModifiableData) => (combatValue.total = combatValue.base + combatValue.mod), + ); + + data.combatValues.hitPoints.max = data.combatValues.hitPoints.total; + } + + /** + * Calculates the total armor value of all equipped items. + */ + private _calculateArmorValueOfEquippedItems(): number { + return this.items + .filter((item) => ["armor", "shield"].includes(item.type)) + .map((item) => item.data.data as DS4Armor | DS4Shield) + .filter((itemData) => itemData.equipped) + .map((itemData) => itemData.armorValue) + .reduce((a, b) => a + b, 0); + } } diff --git a/src/module/actor/sheets/actor-sheet.ts b/src/module/actor/sheets/actor-sheet.ts index 26f6bd8b..3dfda80e 100644 --- a/src/module/actor/sheets/actor-sheet.ts +++ b/src/module/actor/sheets/actor-sheet.ts @@ -14,6 +14,7 @@ export class DS4ActorSheet extends ActorSheet { total?: T; } -export interface ResourceData extends ModifiableData { +export interface ModifiableMaybeData { + base?: T; + mod: T; + total?: T; +} + +export interface ResourceData extends ModifiableMaybeData { value: T; max?: T; } diff --git a/src/module/ds4.ts b/src/module/ds4.ts index 131e6675..cca424f2 100644 --- a/src/module/ds4.ts +++ b/src/module/ds4.ts @@ -7,6 +7,8 @@ import { DS4Check } from "./rolls/check"; import { DS4CharacterActorSheet } from "./actor/sheets/character-sheet"; import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet"; import { createCheckRoll } from "./rolls/check-factory"; +import { registerSystemSettings } from "./settings"; +import { migrate } from "./migrations"; Hooks.once("init", async function () { console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`); @@ -37,6 +39,9 @@ Hooks.once("init", async function () { s: DS4Check, }; + // Register system settings + registerSystemSettings(); + // Register sheet application classes Actors.unregisterSheet("core", ActorSheet); Actors.registerSheet("ds4", DS4CharacterActorSheet, { types: ["character"], makeDefault: true }); @@ -123,3 +128,7 @@ Hooks.once("setup", function () { }, {}); } }); + +Hooks.once("ready", function () { + migrate(); +}); diff --git a/src/module/item/item-data.ts b/src/module/item/item-data.ts index 64ee17f2..d7467f8a 100644 --- a/src/module/item/item-data.ts +++ b/src/module/item/item-data.ts @@ -24,7 +24,7 @@ interface DS4Weapon extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable { opponentDefense: number; } -interface DS4Armor extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable, DS4ItemProtective { +export interface DS4Armor extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable, DS4ItemProtective { armorMaterialType: "cloth" | "leather" | "chain" | "plate"; armorType: "body" | "helmet" | "vambrace" | "greaves" | "vambraceGreaves"; } @@ -57,7 +57,7 @@ interface DS4Spell extends DS4ItemBase, DS4ItemEquipable { scrollPrice: number; } -interface DS4Shield extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable, DS4ItemProtective {} +export interface DS4Shield extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable, DS4ItemProtective {} interface DS4Trinket extends DS4ItemBase, DS4ItemPhysical, DS4ItemEquipable {} interface DS4Equipment extends DS4ItemBase, DS4ItemPhysical {} type DS4RacialAbility = DS4ItemBase; diff --git a/src/module/item/item-sheet.ts b/src/module/item/item-sheet.ts index 8d8df70f..6464defc 100644 --- a/src/module/item/item-sheet.ts +++ b/src/module/item/item-sheet.ts @@ -13,6 +13,7 @@ export class DS4ItemSheet extends ItemSheet { height: 400, classes: ["ds4", "sheet", "item"], tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }], + scrollY: [".sheet-body"], }); } diff --git a/src/module/migrations.ts b/src/module/migrations.ts new file mode 100644 index 00000000..90a11a53 --- /dev/null +++ b/src/module/migrations.ts @@ -0,0 +1,77 @@ +import { migrate as migrate0_1_0 } from "./migrations/0-1-0"; + +export async function migrate(): Promise { + if (!game.user.isGM) { + return; + } + + const currentVersion: string = game.settings.get("ds4", "systemMigrationVersion"); + const targetVersion = game.system.data.version; + + if (isFirstWorldStart(currentVersion)) { + game.settings.set("ds4", "systemMigrationVersion", targetVersion); + return; + } + + if (isNewerVersion(targetVersion, currentVersion)) { + ui.notifications.info( + game.i18n.format("DS4.InfoSystemUpdateStart", { + currentVersion: currentVersion, + targetVersion: targetVersion, + }), + { permanent: true }, + ); + + const migrationFunctionsToExecute = Object.entries(migrations) + .filter(([version]) => !isNewerVersion(currentVersion, version)) // currentVersion <= version of migration + .filter(([version]) => isNewerVersion(targetVersion, version)); // targetVersion > version of migration + + for (const migration of migrationFunctionsToExecute) { + try { + await migration[1](); + } catch (err) { + ui.notifications.error( + game.i18n.format("DS4.ErrorDuringMigration", { + currentVersion: currentVersion, + targetVersion: targetVersion, + migrationVersion: migration[0], + }), + { permanent: true }, + ); + err.message = `Failed ds4 system migration: ${err.message}`; + console.error(err); + return; + } + } + + ui.notifications.info( + game.i18n.format("DS4.InfoSystemUpdateCompleted", { + currentVersion: currentVersion, + targetVersion: targetVersion, + }), + { permanent: true }, + ); + game.settings.set("ds4", "systemMigrationVersion", targetVersion); + return; + } +} + +/** + * The migrations to (potentially) execute when a world is loaded. The key + * specifies the version number _from_ which is being migrated to the next + * version. + * + * @example + * Given the migration version numbers "0.0.1", "0.0.2", "0.1.2", "0.4.9", and + * "0.5.0" an update from version "0.0.2" to "0.5.0" would execute the + * migrations with version "0.0.2", "0.1.2", and "0.4.9". In particular, + * migrations of a version lower than the current version or higher or equal to + * the target version are _not_ executed while all others are. + */ +const migrations: Record Promise> = { + "0.1.0": migrate0_1_0, +}; + +function isFirstWorldStart(version: string): boolean { + return version === ""; +} diff --git a/src/module/migrations/0-1-0.ts b/src/module/migrations/0-1-0.ts new file mode 100644 index 00000000..efc3637e --- /dev/null +++ b/src/module/migrations/0-1-0.ts @@ -0,0 +1,28 @@ +export async function migrate(): Promise { + for (const a of game.actors.entities) { + const updateData = getActorUpdateData(); + console.log(`Migrating actor ${a.name}`); + await a.update(updateData, { enforceTypes: false }); + } +} + +function getActorUpdateData(): Record { + const updateData = { + data: { + combatValues: [ + "hitPoints", + "defense", + "initiative", + "movement", + "meleeAttack", + "rangedAttack", + "spellcasting", + "targetedSpellcasting", + ].reduce((acc, curr) => { + acc[curr] = { "-=base": null }; + return acc; + }, {}), + }, + }; + return updateData; +} diff --git a/src/module/rolls/check-factory.ts b/src/module/rolls/check-factory.ts index 066202d6..c972120c 100644 --- a/src/module/rolls/check-factory.ts +++ b/src/module/rolls/check-factory.ts @@ -33,7 +33,7 @@ class CheckFactory { private checkOptions: DS4CheckFactoryOptions; - async execute(): Promise { + async execute(): Promise { const rollCls: typeof Roll = CONFIG.Dice.rolls[0]; const formula = [ @@ -82,7 +82,7 @@ class CheckFactory { export async function createCheckRoll( targetValue: number, options: Partial = {}, -): Promise { +): Promise { // Ask for additional required data; const gmModifierData = await askGmModifier(targetValue, options); @@ -99,7 +99,7 @@ export async function createCheckRoll( // Possibly additional processing // Execute roll - await cf.execute(); + return cf.execute(); } /** diff --git a/src/module/settings.ts b/src/module/settings.ts new file mode 100644 index 00000000..f449641f --- /dev/null +++ b/src/module/settings.ts @@ -0,0 +1,12 @@ +export function registerSystemSettings(): void { + /** + * Track the system version upon which point a migration was last applied + */ + game.settings.register("ds4", "systemMigrationVersion", { + name: "System Migration Version", + scope: "world", + config: false, + type: String, + default: "", + }); +} diff --git a/src/scss/components/_combat_values.scss b/src/scss/components/_combat_values.scss index 5b7ad3d2..14e412ee 100644 --- a/src/scss/components/_combat_values.scss +++ b/src/scss/components/_combat_values.scss @@ -40,8 +40,9 @@ .combat-value-formula { width: $size; - input { - text-align: center; + text-align: center; + span { + line-height: $default-input-height; } } } diff --git a/src/template.json b/src/template.json index 726d0a56..34b9b253 100644 --- a/src/template.json +++ b/src/template.json @@ -45,36 +45,28 @@ }, "combatValues": { "hitPoints": { - "base": 0, "mod": 0, "value": 0 }, "defense": { - "base": 0, "mod": 0 }, "initiative": { - "base": 0, "mod": 0 }, "movement": { - "base": 0, "mod": 0 }, "meleeAttack": { - "base": 0, "mod": 0 }, "rangedAttack": { - "base": 0, "mod": 0 }, "spellcasting": { - "base": 0, "mod": 0 }, "targetedSpellcasting": { - "base": 0, "mod": 0 } } diff --git a/src/templates/actor/partials/combat-values.hbs b/src/templates/actor/partials/combat-values.hbs index 49bfd024..8ae3eb10 100644 --- a/src/templates/actor/partials/combat-values.hbs +++ b/src/templates/actor/partials/combat-values.hbs @@ -13,9 +13,9 @@
{{combat-value-data.total}}
-
+
{{combat-value-data.base}}+
{{/inline}} @@ -27,4 +27,4 @@ {{> combat-value combat-value-key=combat-value-key combat-value-data=(lookup ../data.combatValues combat-value-key)}} {{/each}} -
+ \ No newline at end of file From e385159f297c623d49c92e720eb4a8ef4c8d7375 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Tue, 19 Jan 2021 03:33:59 +0100 Subject: [PATCH 2/3] readd newline at eof --- src/templates/actor/partials/combat-values.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/templates/actor/partials/combat-values.hbs b/src/templates/actor/partials/combat-values.hbs index 8ae3eb10..d207245a 100644 --- a/src/templates/actor/partials/combat-values.hbs +++ b/src/templates/actor/partials/combat-values.hbs @@ -27,4 +27,4 @@ {{> combat-value combat-value-key=combat-value-key combat-value-data=(lookup ../data.combatValues combat-value-key)}} {{/each}} - \ No newline at end of file + From 60ed168053a1a2e47f87457ddb1d1a7c192cc077 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Wed, 20 Jan 2021 22:11:53 +0100 Subject: [PATCH 3/3] adjust migration versioning --- src/lang/de.json | 6 +- src/lang/en.json | 6 +- src/module/ds4.ts | 5 +- src/module/migrations.ts | 84 +++++++++++----------- src/module/migrations/{0-1-0.ts => 001.ts} | 0 src/module/settings.ts | 6 +- 6 files changed, 56 insertions(+), 51 deletions(-) rename src/module/migrations/{0-1-0.ts => 001.ts} (100%) diff --git a/src/lang/de.json b/src/lang/de.json index fcb1a3d9..f81a5f29 100644 --- a/src/lang/de.json +++ b/src/lang/de.json @@ -175,9 +175,9 @@ "DS4.WarningActorCannotOwnItem": "Der Aktor '{actorName}' vom Typ '{actorType}' kann das Item '{itemName}' vom Typ '{itemType}' nicht besitzen.", "DS4.ErrorDiceCritOverlap": "Es gibt eine Überlappung zwischen Patzern und Immersiegen.", "DS4.ErrorExplodingRecursionLimitExceeded": "Die maximale Rekursionstiefe für slayende Würfelwürfe wurde überschritten.", - "DS4.ErrorDuringMigration": "Fehler während der Aktualisierung des DS4 Systems von Version {currentVersion} auf {targetVersion}. Der Fehler trat während der Ausführung des Migrationsskripts mit der Version {migrationVersion} auf. Spätere Migrationsskripte wurden nicht ausgeführt. Mehr Details finden Sie in der Entwicklerkonsole (F12).", - "DS4.InfoSystemUpdateStart": "Aktualisiere DS4 System von Version {currentVersion} auf {targetVersion}. Bitte haben Sie etwas Geduld, schließen Sie nicht das Spiel und fahren Sie nicht den Server herunter.", - "DS4.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Version {currentVersion} auf {targetVersion} erfolgreich!", + "DS4.ErrorDuringMigration": "Fehler während der Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion}. Der Fehler trat während der Ausführung des Migrationsskripts mit der Version {migrationVersion} auf. Spätere Migrationsskripte wurden nicht ausgeführt. Mehr Details finden Sie in der Entwicklerkonsole (F12).", + "DS4.InfoSystemUpdateStart": "Aktualisiere DS4 System von Migrationsversion {currentVersion} auf {targetVersion}. Bitte haben Sie etwas Geduld, schließen Sie nicht das Spiel und fahren Sie nicht den Server herunter.", + "DS4.InfoSystemUpdateCompleted": "Aktualisierung des DS4 Systems von Migrationsversion {currentVersion} auf {targetVersion} erfolgreich!", "DS4.UnitRounds": "Runden", "DS4.UnitRoundsAbbr": "Rnd", "DS4.UnitMinutes": "Minuten", diff --git a/src/lang/en.json b/src/lang/en.json index fe2c2855..ee1927ee 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -175,9 +175,9 @@ "DS4.WarningActorCannotOwnItem": "The actor '{actorName}' of type '{actorType}' cannot own the item '{itemName}' of type '{itemType}'.", "DS4.ErrorDiceCritOverlap": "There's an overlap between Fumbles and Coups", "DS4.ErrorExplodingRecursionLimitExceeded": "Maximum recursion depth for exploding dice roll exceeded", - "DS4.ErrorDuringMigration": "Error while migrating DS4 system from version {currentVersion} to {targetVersion}. The error occurred during execution of migration script with version {migrationVersion}. Later migrations have not been executed. For more details, please look at the development console (F12).", - "DS4.InfoSystemUpdateStart": "Migrating DS4 system from version {currentVersion} to {targetVersion}. Please be patient and do not close your game or shut down your server.", - "DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from version {currentVersion} to {targetVersion} successful!", + "DS4.ErrorDuringMigration": "Error while migrating DS4 system from migration version {currentVersion} to {targetVersion}. The error occurred during execution of migration script with version {migrationVersion}. Later migrations have not been executed. For more details, please look at the development console (F12).", + "DS4.InfoSystemUpdateStart": "Migrating DS4 system from migration version {currentVersion} to {targetVersion}. Please be patient and do not close your game or shut down your server.", + "DS4.InfoSystemUpdateCompleted": "Migration of DS4 system from migration version {currentVersion} to {targetVersion} successful!", "DS4.UnitRounds": "Rounds", "DS4.UnitRoundsAbbr": "rnd", "DS4.UnitMinutes": "Minutes", diff --git a/src/module/ds4.ts b/src/module/ds4.ts index cca424f2..81115a4c 100644 --- a/src/module/ds4.ts +++ b/src/module/ds4.ts @@ -8,7 +8,7 @@ import { DS4CharacterActorSheet } from "./actor/sheets/character-sheet"; import { DS4CreatureActorSheet } from "./actor/sheets/creature-sheet"; import { createCheckRoll } from "./rolls/check-factory"; import { registerSystemSettings } from "./settings"; -import { migrate } from "./migrations"; +import { migration } from "./migrations"; Hooks.once("init", async function () { console.log(`DS4 | Initializing the DS4 Game System\n${DS4.ASCII}`); @@ -18,6 +18,7 @@ Hooks.once("init", async function () { DS4Item, DS4, createCheckRoll, + migration, }; // Record configuration @@ -130,5 +131,5 @@ Hooks.once("setup", function () { }); Hooks.once("ready", function () { - migrate(); + migration.migrate(); }); diff --git a/src/module/migrations.ts b/src/module/migrations.ts index 90a11a53..ec48294a 100644 --- a/src/module/migrations.ts +++ b/src/module/migrations.ts @@ -1,40 +1,50 @@ -import { migrate as migrate0_1_0 } from "./migrations/0-1-0"; +import { migrate as migrate001 } from "./migrations/001"; -export async function migrate(): Promise { +async function migrate(): Promise { if (!game.user.isGM) { return; } - const currentVersion: string = game.settings.get("ds4", "systemMigrationVersion"); - const targetVersion = game.system.data.version; + const oldMigrationVersion: number = game.settings.get("ds4", "systemMigrationVersion"); - if (isFirstWorldStart(currentVersion)) { - game.settings.set("ds4", "systemMigrationVersion", targetVersion); + const targetMigrationVersion = migrations.length; + + if (isFirstWorldStart(oldMigrationVersion)) { + game.settings.set("ds4", "systemMigrationVersion", targetMigrationVersion); return; } - if (isNewerVersion(targetVersion, currentVersion)) { + return migrateFromTo(oldMigrationVersion, targetMigrationVersion); +} + +async function migrateFromTo(oldMigrationVersion: number, targetMigrationVersion: number): Promise { + if (!game.user.isGM) { + return; + } + + const migrationsToExecute = migrations.slice(oldMigrationVersion, targetMigrationVersion); + + if (migrationsToExecute.length > 0) { ui.notifications.info( game.i18n.format("DS4.InfoSystemUpdateStart", { - currentVersion: currentVersion, - targetVersion: targetVersion, + currentVersion: oldMigrationVersion, + targetVersion: targetMigrationVersion, }), { permanent: true }, ); - const migrationFunctionsToExecute = Object.entries(migrations) - .filter(([version]) => !isNewerVersion(currentVersion, version)) // currentVersion <= version of migration - .filter(([version]) => isNewerVersion(targetVersion, version)); // targetVersion > version of migration - - for (const migration of migrationFunctionsToExecute) { + for (const [i, migration] of migrationsToExecute.entries()) { + const currentMigrationVersion = oldMigrationVersion + i + 1; + console.log("executing migration script ", currentMigrationVersion); try { - await migration[1](); + await migration(); + game.settings.set("ds4", "systemMigrationVersion", currentMigrationVersion); } catch (err) { ui.notifications.error( game.i18n.format("DS4.ErrorDuringMigration", { - currentVersion: currentVersion, - targetVersion: targetVersion, - migrationVersion: migration[0], + currentVersion: oldMigrationVersion, + targetVersion: targetMigrationVersion, + migrationVersion: currentMigrationVersion, }), { permanent: true }, ); @@ -46,32 +56,26 @@ export async function migrate(): Promise { ui.notifications.info( game.i18n.format("DS4.InfoSystemUpdateCompleted", { - currentVersion: currentVersion, - targetVersion: targetVersion, + currentVersion: oldMigrationVersion, + targetVersion: targetMigrationVersion, }), { permanent: true }, ); - game.settings.set("ds4", "systemMigrationVersion", targetVersion); - return; } } -/** - * The migrations to (potentially) execute when a world is loaded. The key - * specifies the version number _from_ which is being migrated to the next - * version. - * - * @example - * Given the migration version numbers "0.0.1", "0.0.2", "0.1.2", "0.4.9", and - * "0.5.0" an update from version "0.0.2" to "0.5.0" would execute the - * migrations with version "0.0.2", "0.1.2", and "0.4.9". In particular, - * migrations of a version lower than the current version or higher or equal to - * the target version are _not_ executed while all others are. - */ -const migrations: Record Promise> = { - "0.1.0": migrate0_1_0, -}; - -function isFirstWorldStart(version: string): boolean { - return version === ""; +function getTargetMigrationVersion(): number { + return migrations.length; } + +const migrations: Array<() => Promise> = [migrate001]; + +function isFirstWorldStart(migrationVersion: number): boolean { + return migrationVersion < 0; +} + +export const migration = { + migrate: migrate, + migrateFromTo: migrateFromTo, + getTargetMigrationVersion: getTargetMigrationVersion, +}; diff --git a/src/module/migrations/0-1-0.ts b/src/module/migrations/001.ts similarity index 100% rename from src/module/migrations/0-1-0.ts rename to src/module/migrations/001.ts diff --git a/src/module/settings.ts b/src/module/settings.ts index f449641f..644033f0 100644 --- a/src/module/settings.ts +++ b/src/module/settings.ts @@ -1,12 +1,12 @@ export function registerSystemSettings(): void { /** - * Track the system version upon which point a migration was last applied + * 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: String, - default: "", + type: Number, + default: -1, }); }