Files
DeltaGreen/module/applications/profession-setup-flow.js

187 lines
5.6 KiB
JavaScript

import showPickSkillsDialog from "./add-profession-dialog.js";
import showAssignStatsDialog from "./assign-stats-dialog.js";
import showDamagedVeteransDialog from "./damaged-veterans-dialog.js";
import showHardExperienceDialog from "./hard-experience-dialog.js";
import showPickStatisticsDialog from "./pick-statistics-dialog.js";
import showRollStatsDialog from "./roll-stats-dialog.js";
import showThingsDisorderDialog from "./things-disorder-dialog.js";
import { commitProfessionSetup } from "../profession/index.js";
/**
* @param {Actor} actor
* @param {object} itemData
* @returns {Item}
*/
function createProfessionStub(actor, itemData) {
const data = foundry.utils.duplicate(itemData);
delete data._id;
const ItemDocument = foundry.utils.getDocumentClass("Item");
return new ItemDocument(data, { parent: actor });
}
/**
* @param {Actor} actor
* @param {object} [options]
* @param {TokenDocument|null} [options.token]
* @returns {Promise<boolean>}
*/
async function runStatsPhase(actor, { token = null } = {}) {
const choice = await showPickStatisticsDialog();
if (choice === null) return false;
if (choice === "skip") return true;
if (choice === "roll") {
const result = await showRollStatsDialog(actor, { token });
if (result === null) return false;
if (result.outcome === "back") return runStatsPhase(actor, { token });
return true;
}
if (choice === "assign") {
const result = await showAssignStatsDialog(actor);
if (result === null) return false;
if (result.outcome === "back") return runStatsPhase(actor, { token });
return true;
}
return false;
}
/**
* @param {import("../profession/commit-character-creation.js").CharacterCreationPayload} payload
* @param {object} [options]
* @param {string} [options.selectedPath]
* @param {import("./hard-experience-dialog.js").HardExperienceDialogState} [options.heState]
* @param {import("./things-disorder-dialog.js").ThingsDisorderDialogState} [options.tdState]
* @returns {Promise<object|null>}
*/
async function resolveVeteranResult(
payload,
{ selectedPath, heState, tdState } = {},
) {
const dvResult = await showDamagedVeteransDialog(payload, { selectedPath });
if (dvResult === null) return null;
if (dvResult.outcome === "back") return { outcome: "back" };
const { path } = dvResult;
if (path === "hardExperience") {
const heResult = await showHardExperienceDialog(payload, heState);
if (heResult === null) return null;
if (heResult.outcome === "back") {
return resolveVeteranResult(payload, {
selectedPath: "hardExperience",
heState: {
bonusCatalogIds: heResult.bonusCatalogIds,
bonusTypedLabels: heResult.bonusTypedLabels,
removedBondIndex: heResult.removedBondIndex,
},
});
}
return {
path: "hardExperience",
bonusCatalogIds: heResult.bonusCatalogIds,
bonusTypedLabels: heResult.bonusTypedLabels,
removedBondIndex: heResult.removedBondIndex,
};
}
if (path === "thingsMan") {
const tdResult = await showThingsDisorderDialog(tdState);
if (tdResult === null) return null;
if (tdResult.outcome === "back") {
return resolveVeteranResult(payload, {
selectedPath: "thingsMan",
tdState: { disorder: tdResult.disorder },
});
}
return {
path: "thingsMan",
disorder: tdResult.disorder,
};
}
return { path };
}
/**
* @param {Item} professionItem
* @param {Actor} actor
* @param {import("./add-profession-dialog.js").CharacterCreationDraft} [characterDraft]
* @returns {Promise<boolean>}
*/
async function runCharacterCreationPhase(
professionItem,
actor,
characterDraft,
) {
const ccResult = await showPickSkillsDialog(professionItem, actor, {
draft: characterDraft,
});
if (ccResult === null) return false;
const veteranResult = await resolveVeteranResult(ccResult.payload);
if (veteranResult === null) return false;
if (veteranResult.outcome === "back") {
return runCharacterCreationPhase(professionItem, actor, ccResult.draft);
}
await commitProfessionSetup(
actor,
professionItem,
ccResult.payload,
veteranResult,
);
return true;
}
/**
* Run the full profession setup flow: Pick Statistics → optional stat setup → Character Creation → Damaged Veterans.
*
* @param {Item} professionItem Profession item or unsaved stub on the actor
* @param {Actor} actor
* @param {object} [options]
* @param {TokenDocument|null} [options.token]
* @returns {Promise<boolean>} True if the full setup flow completed successfully.
*/
async function runProfessionSetupFlow(
professionItem,
actor,
{ token = null } = {},
) {
const statsComplete = await runStatsPhase(actor, { token });
if (!statsComplete) return false;
return runCharacterCreationPhase(professionItem, actor);
}
/**
* Assign a profession to an agent: full setup flow, then embed the item only on success.
*
* @param {Actor} actor
* @param {object} itemData Item source data (e.g. from drop `toObject()` or create template)
* @param {object} [options]
* @param {TokenDocument|null} [options.token]
* @returns {Promise<Item|null>}
*/
export default async function assignProfessionToAgent(
actor,
itemData,
{ token = null } = {},
) {
if (actor.items.some((i) => i.type === "profession")) {
ui.notifications.info(game.i18n.localize("DG.Profession.AlreadyAssigned"), {
localize: false,
});
return null;
}
const stub = createProfessionStub(actor, itemData);
const submitted = await runProfessionSetupFlow(stub, actor, { token });
if (!submitted) return null;
return actor.items.find((item) => item.type === "profession") ?? null;
}