import FTLNomadActorSheet from "./base-actor-sheet.mjs" export default class FTLNomadStarshipSheet extends FTLNomadActorSheet { /** @override */ static DEFAULT_OPTIONS = { classes: ["starship"], position: { width: 680, height: 640, }, window: { contentClasses: ["starship-content"], }, actions: { createEquipment: FTLNomadStarshipSheet.#onCreateEquipment, createWeapon: FTLNomadStarshipSheet.#onCreateWeapon, removeCrew: FTLNomadStarshipSheet.#onRemoveCrew, viewCrew: FTLNomadStarshipSheet.#onViewCrew, pilotCrew: FTLNomadStarshipSheet.#onPilotCrew, rollNpcCrew: FTLNomadStarshipSheet.#onRollNpcCrew, }, } /** * Remove a crew member from the starship * @param {Event} event The initiating click event * @param {HTMLElement} target The current target of the event listener */ static async #onRemoveCrew(event, target) { const crewUuid = target.dataset.crewUuid const actor = await fromUuid(crewUuid) // Show confirmation dialog const confirmed = await foundry.applications.api.DialogV2.confirm({ window: { title: game.i18n.localize("FTLNOMAD.RemoveCrewTitle") }, content: game.i18n.format("FTLNOMAD.RemoveCrewContent", { name: actor?.name || "Unknown" }), rejectClose: false, modal: true }) if (!confirmed) return const currentCrew = this.document.system.crewList || [] const updatedCrew = currentCrew.filter(uuid => uuid !== crewUuid) await this.document.update({ "system.crewList": updatedCrew }) } /** * Open the actor sheet of a crew member * @param {Event} event The initiating click event * @param {HTMLElement} target The current target of the event listener */ static async #onViewCrew(event, target) { const actorUuid = target.closest('.crew-member').dataset.actorUuid const actor = await fromUuid(actorUuid) if (actor) { actor.sheet.render(true) } } /** * Roll a piloting check for a crew member with starship agility bonus * @param {Event} event The initiating click event * @param {HTMLElement} target The current target of the event listener */ static async #onPilotCrew(event, target) { const actorUuid = target.closest('.crew-member').dataset.actorUuid const actor = await fromUuid(actorUuid) console.log("Pilot crew - Actor:", actor, "Type:", actor?.type) if (!actor) { ui.notifications.warn("Actor not found") return } if (actor.type !== "character") { ui.notifications.warn(`This actor is of type "${actor.type}", not "character"`) return } // Get starship agility bonus const starshipAgility = this.document.system.agility || 0 // Get all skills from the actor const actorSkills = actor.system.skills if (!actorSkills) { ui.notifications.warn(game.i18n.localize("FTLNOMAD.Warning.noSkills")) return } // Default to vehicles skill const defaultSkill = actorSkills.vehicles || Object.values(actorSkills)[0] // Import the Roll class const FTLNomadRoll = (await import("../../documents/roll.mjs")).default // Call the roll prompt with all skills available and starship agility as a vehicle bonus let roll = await FTLNomadRoll.prompt({ rollType: "skill", rollItem: defaultSkill, availableSkills: actorSkills, selectedSkillId: "vehicles", actorId: actor.id, actorName: actor.name, actorImage: actor.img, talents: actor.items.filter(i => i.type === "talent" && i.system.isAdvantage), isEncumbered: actor.system.isEncumbered(), hasTarget: false, vehicleBonus: starshipAgility }) if (!roll) return null await roll.toMessage({}, { rollMode: roll.options.rollMode }) } /** * Roll a check for NPC crew with starship agility bonus * @param {Event} event The initiating click event * @param {HTMLElement} target The current target of the event listener */ static async #onRollNpcCrew(event, target) { const npcCrewLevel = this.document.system.npcCrew || 0 if (npcCrewLevel < 0) { ui.notifications.warn(game.i18n.localize("FTLNOMAD.Warning.noNpcCrew")) return } // Get starship agility bonus const starshipAgility = this.document.system.agility || 0 // Create a fake skill object for the NPC crew const npcSkill = { value: npcCrewLevel, label: "FTLNOMAD.Label.npcCrewSkill" } // Import the Roll class const FTLNomadRoll = (await import("../../documents/roll.mjs")).default // Call the roll prompt with NPC crew level as skill let roll = await FTLNomadRoll.prompt({ rollType: "skill", rollItem: npcSkill, actorId: this.document.id, actorName: `${this.document.name} (NPC Crew)`, actorImage: this.document.img, talents: [], isEncumbered: false, hasTarget: false, vehicleBonus: starshipAgility }) if (!roll) return null await roll.toMessage({}, { rollMode: roll.options.rollMode }) } /** @override */ static PARTS = { main: { template: "systems/fvtt-ftl-nomad/templates/starship-main.hbs", }, tabs: { template: "templates/generic/tab-navigation.hbs", }, equipment: { template: "systems/fvtt-ftl-nomad/templates/starship-equipment.hbs", }, description: { template: "systems/fvtt-ftl-nomad/templates/starship-description.hbs", }, } /** @override */ tabGroups = { sheet: "equipment", } /** * Prepare an array of form header tabs. * @returns {Record>} */ #getTabs() { const tabs = { equipment: { id: "equipment", group: "sheet", icon: "fa-solid fa-shapes", label: "FTLNOMAD.Label.equipment" }, description: { id: "description", group: "sheet", icon: "fa-solid fa-book", label: "FTLNOMAD.Label.description" }, } for (const v of Object.values(tabs)) { v.active = this.tabGroups[v.group] === v.id v.cssClass = v.active ? "active" : "" } return tabs } /** @override */ async _prepareContext() { const context = await super._prepareContext() context.tabs = this.#getTabs() context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }) context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true }) context.enrichedModifications = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.modifications, { async: true }) // Prepare crew members data context.crewMembers = await this.#prepareCrewMembers() return context } /** * Prepare crew members data from stored UUIDs * @returns {Promise} */ async #prepareCrewMembers() { const crewUuids = this.document.system.crewList || [] const crewMembers = [] for (const uuid of crewUuids) { const actor = await fromUuid(uuid) if (actor) { crewMembers.push({ uuid: uuid, id: actor.id, name: actor.name, img: actor.img }) } } return crewMembers } _generateTooltip(type, target) { } /** @override */ async _preparePartContext(partId, context) { const doc = this.document switch (partId) { case "main": break case "equipment": context.tab = context.tabs.equipment context.weapons = doc.itemTypes.weapon context.weapons.sort((a, b) => a.name.localeCompare(b.name)) context.equipments = doc.itemTypes.equipment context.equipments.sort((a, b) => a.name.localeCompare(b.name)) break case "description": context.tab = context.tabs.description context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.description, { async: true }) context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(doc.system.notes, { async: true }) break } return context } static #onCreateEquipment(event, target) { this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("FTLNOMAD.Label.newEquipment"), type: "equipment" }]) } static #onCreateWeapon(event, target) { this.document.createEmbeddedDocuments("Item", [{ name: game.i18n.localize("FTLNOMAD.Label.newWeapon"), type: "weapon" }]) } async _onRoll(event, target) { let rollType = $(event.currentTarget).data("roll-type") let item console.log("rollType", rollType) switch (rollType) { case "damage": let li = $(event.currentTarget).parents(".item"); item = this.actor.items.get(li.data("item-id")); break case "starship-guns": item = { name: "Starship Guns", type: "weapon", system: { damage: this.actor.system.guns, rangeType: "heavyweapon" } } rollType = "damage" break default: throw new Error(`Unknown roll type ${rollType}`) } await this.document.system.roll(rollType, item) } async _onDrop(event) { if (!this.isEditable || !this.isEditMode) return const data = TextEditor.getDragEventData(event) // Handle different data types switch (data.type) { case "Item": const item = await fromUuid(data.uuid) return super._onDropItem(item) case "Actor": const actor = await fromUuid(data.uuid) return this.#onDropActor(actor) } } /** * Handle dropping an actor onto the starship sheet to add them to the crew * @param {Actor} actor The actor being dropped */ async #onDropActor(actor) { if (!actor) return // Check if actor is of type "character" if (actor.type !== "character") { ui.notifications.info(game.i18n.localize("FTLNOMAD.Warning.onlyCharactersAllowed")) return } // Check if the actor has a linked prototype if (!actor.prototypeToken?.actorLink) { ui.notifications.info(game.i18n.localize("FTLNOMAD.Warning.noLinkedPrototype")) return } const currentCrew = this.document.system.crewList || [] // Check if actor is already in crew if (currentCrew.includes(actor.uuid)) { ui.notifications.warn(game.i18n.localize("FTLNOMAD.Warning.alreadyInCrew")) return } // Add actor UUID to crew array const updatedCrew = [...currentCrew, actor.uuid] await this.document.update({ "system.crewList": updatedCrew }) } }