Add crew member option with pilot rolls integrated
All checks were successful
Release Creation / build (release) Successful in 48s

This commit is contained in:
2025-11-09 22:12:00 +01:00
parent c180365a61
commit 27b09d4546
30 changed files with 687 additions and 85 deletions

View File

@@ -1155,6 +1155,65 @@ i.fvtt-ftl-nomad {
font-size: calc(var(--font-size-standard) * 1.4); font-size: calc(var(--font-size-standard) * 1.4);
padding-left: 5px; padding-left: 5px;
} }
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
padding: 4px;
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member {
display: flex;
align-items: center;
gap: 4px;
padding: 4px;
border: 1px solid var(--color-border-dark);
border-radius: 4px;
background: var(--color-light-2);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .crew-img {
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid var(--color-border-dark);
cursor: pointer;
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .crew-img:hover {
opacity: 0.8;
box-shadow: 0 0 4px var(--color-shadow-primary);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .crew-name {
flex: 1;
font-size: var(--font-size-small);
cursor: pointer;
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .crew-name:hover {
color: var(--color-text-dark-highlight);
text-shadow: 0 0 4px var(--color-shadow-primary);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .controls {
display: flex;
gap: 4px;
min-width: 3rem;
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .controls a {
color: var(--color-text-dark-primary);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .controls a:hover {
color: var(--color-text-dark-highlight);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .controls .pilot-button {
color: var(--color-text-light-primary);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-member .controls .pilot-button:hover {
color: var(--color-text-light-highlight);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .crew .crew-empty {
grid-column: 1 / -1;
text-align: center;
padding: 8px;
font-style: italic;
color: var(--color-text-dark-secondary);
}
.fvtt-ftl-nomad .tab.vehicle-equipment .main-div .weapons { .fvtt-ftl-nomad .tab.vehicle-equipment .main-div .weapons {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
@@ -1845,6 +1904,65 @@ i.fvtt-ftl-nomad {
font-size: calc(var(--font-size-standard) * 1.4); font-size: calc(var(--font-size-standard) * 1.4);
padding-left: 5px; padding-left: 5px;
} }
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
padding: 4px;
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member {
display: flex;
align-items: center;
gap: 4px;
padding: 4px;
border: 1px solid var(--color-border-dark);
border-radius: 4px;
background: var(--color-light-2);
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .crew-img {
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid var(--color-border-dark);
cursor: pointer;
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .crew-img:hover {
opacity: 0.8;
box-shadow: 0 0 4px var(--color-shadow-primary);
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .crew-name {
flex: 1;
font-size: var(--font-size-small);
cursor: pointer;
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .crew-name:hover {
color: var(--color-text-dark-highlight);
text-shadow: 0 0 4px var(--color-shadow-primary);
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .controls {
display: flex;
gap: 4px;
min-width: 3rem;
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .controls a {
color: var(--color-text-dark-primary);
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .controls a:hover {
color: var(--color-text-dark-highlight);
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .controls .pilot-button {
color: var(--color-text-light-primary);
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-member .controls .pilot-button:hover {
color: var(--color-text-light-highlight);
}
.fvtt-ftl-nomad .tab.starship-equipment .crew .crew-empty {
grid-column: 1 / -1;
text-align: center;
padding: 8px;
font-style: italic;
color: var(--color-text-dark-secondary);
}
.fvtt-ftl-nomad .tab.starship-equipment .weapons { .fvtt-ftl-nomad .tab.starship-equipment .weapons {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);

View File

@@ -524,7 +524,10 @@
"titleWeapon": "Weapon Roll", "titleWeapon": "Weapon Roll",
"total": "Total", "total": "Total",
"totalScore": "Total Score", "totalScore": "Total Score",
"weapons": "Weapons" "weapons": "Weapons",
"crew": "Crew",
"noCrew": "No crew members assigned",
"vehicleBonus": "Vehicle/Starship Agility Bonus"
}, },
"Language": { "Language": {
"FIELDS": { "FIELDS": {
@@ -633,6 +636,9 @@
"crew": { "crew": {
"label": "Crew" "label": "Crew"
}, },
"crewCapacity": {
"label": "Crew Capacity"
},
"description": { "description": {
"label": "Description" "label": "Description"
}, },
@@ -712,6 +718,9 @@
"crew": { "crew": {
"label": "Crew" "label": "Crew"
}, },
"crewCapacity": {
"label": "Crew Capacity"
},
"description": { "description": {
"label": "Description" "label": "Description"
}, },
@@ -723,7 +732,18 @@
} }
} }
}, },
"Warning": {}, "Warning": {
"alreadyInCrew": "This actor is already a crew member",
"notACharacter": "Only characters can pilot vehicles",
"noVehiclesSkill": "This character does not have the vehicles skill",
"onlyCharactersAllowed": "Only characters can be added as crew members",
"noLinkedPrototype": "This character must be linked to his token to be added as crew member"
},
"RemoveCrew": "Remove from crew",
"RemoveCrewTitle": "Remove crew member?",
"RemoveCrewContent": "Are you sure you want to remove {name} from the crew?",
"PilotVehicle": "Pilot vehicle (with vehicle agility bonus)",
"PilotStarship": "Pilot starship (with starship agility bonus)",
"Weapon": { "Weapon": {
"FIELDS": { "FIELDS": {
"cost": { "cost": {

View File

@@ -14,9 +14,100 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
actions: { actions: {
createEquipment: FTLNomadStarshipSheet.#onCreateEquipment, createEquipment: FTLNomadStarshipSheet.#onCreateEquipment,
createWeapon: FTLNomadStarshipSheet.#onCreateWeapon, createWeapon: FTLNomadStarshipSheet.#onCreateWeapon,
removeCrew: FTLNomadStarshipSheet.#onRemoveCrew,
viewCrew: FTLNomadStarshipSheet.#onViewCrew,
pilotCrew: FTLNomadStarshipSheet.#onPilotCrew,
}, },
} }
/**
* 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 the vehicles skill from the actor
const vehiclesSkill = actor.system.skills?.vehicles
if (!vehiclesSkill) {
ui.notifications.warn(game.i18n.localize("FTLNOMAD.Warning.noVehiclesSkill"))
return
}
// Import the Roll class
const FTLNomadRoll = (await import("../../documents/roll.mjs")).default
// Call the roll prompt with the starship agility as a vehicle bonus
let roll = await FTLNomadRoll.prompt({
rollType: "skill",
rollItem: vehiclesSkill,
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 })
}
/** @override */ /** @override */
static PARTS = { static PARTS = {
main: { main: {
@@ -63,9 +154,35 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
context.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { 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 }) 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 return context
} }
/**
* Prepare crew members data from stored UUIDs
* @returns {Promise<Array>}
*/
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) { _generateTooltip(type, target) {
} }
@@ -128,7 +245,41 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
case "Item": case "Item":
const item = await fromUuid(data.uuid) const item = await fromUuid(data.uuid)
return super._onDropItem(item) 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 })
}
} }

View File

@@ -14,9 +14,100 @@ export default class FTLNomadVehicleSheet extends FTLNomadActorSheet {
actions: { actions: {
createEquipment: FTLNomadVehicleSheet.#onCreateEquipment, createEquipment: FTLNomadVehicleSheet.#onCreateEquipment,
createWeapon: FTLNomadVehicleSheet.#onCreateWeapon, createWeapon: FTLNomadVehicleSheet.#onCreateWeapon,
removeCrew: FTLNomadVehicleSheet.#onRemoveCrew,
viewCrew: FTLNomadVehicleSheet.#onViewCrew,
pilotCrew: FTLNomadVehicleSheet.#onPilotCrew,
}, },
} }
/**
* Remove a crew member from the vehicle
* @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 vehicle 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 vehicle agility bonus
const vehicleAgility = this.document.system.agility || 0
// Get the vehicles skill from the actor
const vehiclesSkill = actor.system.skills?.vehicles
if (!vehiclesSkill) {
ui.notifications.warn(game.i18n.localize("FTLNOMAD.Warning.noVehiclesSkill"))
return
}
// Import the Roll class
const FTLNomadRoll = (await import("../../documents/roll.mjs")).default
// Call the roll prompt with the vehicle agility as a vehicle bonus
let roll = await FTLNomadRoll.prompt({
rollType: "skill",
rollItem: vehiclesSkill,
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: vehicleAgility
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
/** @override */ /** @override */
static PARTS = { static PARTS = {
main: { main: {
@@ -62,9 +153,35 @@ export default class FTLNomadVehicleSheet extends FTLNomadActorSheet {
context.enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.description, { async: true }) 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.enrichedNotes = await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.document.system.notes, { async: true })
// Prepare crew members data
context.crewMembers = await this.#prepareCrewMembers()
return context return context
} }
/**
* Prepare crew members data from stored UUIDs
* @returns {Promise<Array>}
*/
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) { _generateTooltip(type, target) {
} }
@@ -128,7 +245,36 @@ export default class FTLNomadVehicleSheet extends FTLNomadActorSheet {
case "Item": case "Item":
const item = await fromUuid(data.uuid) const item = await fromUuid(data.uuid)
return super._onDropItem(item) return super._onDropItem(item)
case "Actor":
const actor = await fromUuid(data.uuid)
return this.#onDropActor(actor)
} }
} }
/**
* Handle dropping an actor onto the vehicle 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
}
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 })
}
} }

View File

@@ -67,9 +67,12 @@ export default class FTLNomadRoll extends Roll {
} else { } else {
let mod = options.rollItem?.value || 0 let mod = options.rollItem?.value || 0
fullFormula = `${options.formula} + ${options.skillModifier}D + ${mod} + ${options.rangeModifier}D + ${options.numericModifier}D + ${options.numericModifierSelect}` fullFormula = `${options.formula} + ${options.skillModifier}D + ${mod} + ${options.rangeModifier}D + ${options.numericModifier}D + ${options.numericModifierSelect}`
if (options.vehicleBonus) {
fullFormula += ` + ${options.vehicleBonus}`
}
} }
// Replace all the "+ -" with "-" // Replace all the "+ -" with "-"
fullFormula = fullFormula.replace(/\+\s*\-/g, "- ") fullFormula = fullFormula.replace(/\+\s*-/g, "- ")
$('#roll-dialog-full-formula').text(fullFormula) $('#roll-dialog-full-formula').text(fullFormula)
options.fullFormula = fullFormula options.fullFormula = fullFormula
} }
@@ -136,10 +139,14 @@ export default class FTLNomadRoll extends Roll {
options.numericModifierSelect = 0 options.numericModifierSelect = 0
options.rangeModifier = rangeModifier options.rangeModifier = rangeModifier
options.damageModifier = damageModifier options.damageModifier = damageModifier
options.vehicleBonus = options.vehicleBonus || 0
let fullFormula = `${formula} + ${options.rollItem.value}` let fullFormula = `${formula} + ${options.rollItem.value}`
if (options.isEncumbered) { if (options.isEncumbered) {
fullFormula += ` - 1D` fullFormula += ` - 1D`
} }
if (options.vehicleBonus) {
fullFormula += ` + ${options.vehicleBonus}`
}
options.fullFormula = fullFormula options.fullFormula = fullFormula
options.formula = formula options.formula = formula
@@ -164,6 +171,7 @@ export default class FTLNomadRoll extends Roll {
hasTarget: options.hasTarget, hasTarget: options.hasTarget,
modifier, modifier,
numericModifierSelect, numericModifierSelect,
vehicleBonus: options.vehicleBonus,
} }
const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-ftl-nomad/templates/roll-dialog.hbs", dialogContext) const content = await foundry.applications.handlebars.renderTemplate("systems/fvtt-ftl-nomad/templates/roll-dialog.hbs", dialogContext)
@@ -236,6 +244,7 @@ export default class FTLNomadRoll extends Roll {
options.finalModifier = options.numericModifier + options.skillModifier + options.rangeModifier options.finalModifier = options.numericModifier + options.skillModifier + options.rangeModifier
let mod = options.rollItem?.value || 0 let mod = options.rollItem?.value || 0
mod += options.numericModifierSelect mod += options.numericModifierSelect
mod += options.vehicleBonus || 0
// Build the dice formula // Build the dice formula
let diceFormula = "2d6" let diceFormula = "2d6"

View File

@@ -11,7 +11,8 @@ export default class FTLNomadStarship extends foundry.abstract.TypeDataModel {
schema.hullType = new fields.StringField({ required: true, initial: "small", choices: SYSTEM.STARSHIP_HULL }) schema.hullType = new fields.StringField({ required: true, initial: "small", choices: SYSTEM.STARSHIP_HULL })
schema.endurance = new fields.StringField({ required: true, initial: "" }) schema.endurance = new fields.StringField({ required: true, initial: "" })
schema.armor = new fields.StringField({ required: true, initial: "" }) schema.armor = new fields.StringField({ required: true, initial: "" })
schema.crew = new fields.StringField({ required: true, initial: "" }) schema.crewList = new fields.ArrayField(new fields.StringField({ required: true }), { initial: [] })
schema.crewCapacity = new fields.StringField({ required: true, initial: "" })
schema.cargo = new fields.StringField({ required: true, initial: "" }) schema.cargo = new fields.StringField({ required: true, initial: "" })
schema.guns = new fields.StringField({ required: true, initial: "1d6" }) schema.guns = new fields.StringField({ required: true, initial: "1d6" })
schema.travelMultiplier = new fields.StringField({ required: true, initial: "" }) schema.travelMultiplier = new fields.StringField({ required: true, initial: "" })

View File

@@ -10,7 +10,8 @@ export default class FTLNomadVehicle extends foundry.abstract.TypeDataModel {
schema.agility = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }) schema.agility = new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
schema.armor = new fields.StringField({ required: true, initial: "" }) schema.armor = new fields.StringField({ required: true, initial: "" })
schema.cargo = new fields.StringField({ required: true, initial: "" }) schema.cargo = new fields.StringField({ required: true, initial: "" })
schema.crew = new fields.StringField({ required: true, initial: "" }) schema.crewList = new fields.ArrayField(new fields.StringField({ required: true }), { initial: [] })
schema.crewCapacity = new fields.StringField({ required: true, initial: "" })
schema.force = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 }) schema.force = new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 })
schema.range = new fields.StringField({ required: true, initial: "1d6" }) schema.range = new fields.StringField({ required: true, initial: "1d6" })
schema.speed = new fields.StringField({ required: true, initial: "1d6" }) schema.speed = new fields.StringField({ required: true, initial: "1d6" })

View File

@@ -1 +1 @@
MANIFEST-000107 MANIFEST-000120

View File

@@ -1,7 +1,7 @@
2025/10/27-20:06:28.536125 7f2a23fff6c0 Recovering log #105 2025/11/09-22:04:33.775487 7f0921ffb6c0 Recovering log #117
2025/10/27-20:06:28.547660 7f2a23fff6c0 Delete type=3 #103 2025/11/09-22:04:33.868329 7f0921ffb6c0 Delete type=3 #115
2025/10/27-20:06:28.547796 7f2a23fff6c0 Delete type=0 #105 2025/11/09-22:04:33.868406 7f0921ffb6c0 Delete type=0 #117
2025/10/27-20:07:11.806730 7f2a213ff6c0 Level-0 table #110: started 2025/11/09-22:11:34.495817 7f0920bff6c0 Level-0 table #123: started
2025/10/27-20:07:11.806798 7f2a213ff6c0 Level-0 table #110: 0 bytes OK 2025/11/09-22:11:34.495875 7f0920bff6c0 Level-0 table #123: 0 bytes OK
2025/10/27-20:07:11.813240 7f2a213ff6c0 Delete type=0 #108 2025/11/09-22:11:34.530303 7f0920bff6c0 Delete type=0 #121
2025/10/27-20:07:11.813543 7f2a213ff6c0 Manual compaction at level-0 from '!folders!AuBtSOj1mJmh88qx' @ 72057594037927935 : 1 .. '!items!zv9dwgL3p7ThQn7j' @ 0 : 0; will stop at (end) 2025/11/09-22:11:34.530590 7f0920bff6c0 Manual compaction at level-0 from '!folders!AuBtSOj1mJmh88qx' @ 72057594037927935 : 1 .. '!items!zv9dwgL3p7ThQn7j' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,11 @@
2025/10/27-20:03:18.716726 7f2a22ffd6c0 Recovering log #101 2025/11/09-19:53:34.147030 7f09217fa6c0 Delete type=3 #1
2025/10/27-20:03:18.728612 7f2a22ffd6c0 Delete type=3 #99 2025/11/09-22:04:27.202802 7f0920bff6c0 Level-0 table #118: started
2025/10/27-20:03:18.728755 7f2a22ffd6c0 Delete type=0 #101 2025/11/09-22:04:27.202844 7f0920bff6c0 Level-0 table #118: 0 bytes OK
2025/10/27-20:06:09.635606 7f2a213ff6c0 Level-0 table #106: started 2025/11/09-22:04:27.240256 7f0920bff6c0 Delete type=0 #116
2025/10/27-20:06:09.635675 7f2a213ff6c0 Level-0 table #106: 0 bytes OK 2025/11/09-22:04:27.339388 7f0920bff6c0 Manual compaction at level-0 from '!folders!AuBtSOj1mJmh88qx' @ 72057594037927935 : 1 .. '!items!zv9dwgL3p7ThQn7j' @ 0 : 0; will stop at '!items!zv9dwgL3p7ThQn7j' @ 385 : 1
2025/10/27-20:06:09.644064 7f2a213ff6c0 Delete type=0 #104 2025/11/09-22:04:27.339402 7f0920bff6c0 Compacting 1@0 + 0@1 files
2025/10/27-20:06:09.658912 7f2a213ff6c0 Manual compaction at level-0 from '!folders!AuBtSOj1mJmh88qx' @ 72057594037927935 : 1 .. '!items!zv9dwgL3p7ThQn7j' @ 0 : 0; will stop at (end) 2025/11/09-22:04:27.363062 7f0920bff6c0 Generated table #119@0: 316 keys, 126470 bytes
2025/11/09-22:04:27.363092 7f0920bff6c0 Compacted 1@0 + 0@1 files => 126470 bytes
2025/11/09-22:04:27.437323 7f0920bff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/11/09-22:04:27.437448 7f0920bff6c0 Delete type=2 #90
2025/11/09-22:04:27.535013 7f0920bff6c0 Manual compaction at level-0 from '!items!zv9dwgL3p7ThQn7j' @ 385 : 1 .. '!items!zv9dwgL3p7ThQn7j' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

View File

@@ -1 +1 @@
MANIFEST-000085 MANIFEST-000098

View File

@@ -1,7 +1,7 @@
2025/10/27-20:06:28.555946 7f2a227fc6c0 Recovering log #83 2025/11/09-22:04:33.872006 7f09217fa6c0 Recovering log #95
2025/10/27-20:06:28.567241 7f2a227fc6c0 Delete type=3 #81 2025/11/09-22:04:33.928521 7f09217fa6c0 Delete type=3 #93
2025/10/27-20:06:28.567394 7f2a227fc6c0 Delete type=0 #83 2025/11/09-22:04:33.928601 7f09217fa6c0 Delete type=0 #95
2025/10/27-20:07:11.791873 7f2a213ff6c0 Level-0 table #88: started 2025/11/09-22:11:34.382951 7f0920bff6c0 Level-0 table #101: started
2025/10/27-20:07:11.791940 7f2a213ff6c0 Level-0 table #88: 0 bytes OK 2025/11/09-22:11:34.383012 7f0920bff6c0 Level-0 table #101: 0 bytes OK
2025/10/27-20:07:11.799353 7f2a213ff6c0 Delete type=0 #86 2025/11/09-22:11:34.414745 7f0920bff6c0 Delete type=0 #99
2025/10/27-20:07:11.813499 7f2a213ff6c0 Manual compaction at level-0 from '!actors!0FQ6XaRi24OorI21' @ 72057594037927935 : 1 .. '!folders!vRnrOJqSMlxbSgyX' @ 0 : 0; will stop at (end) 2025/11/09-22:11:34.530532 7f0920bff6c0 Manual compaction at level-0 from '!actors!0FQ6XaRi24OorI21' @ 72057594037927935 : 1 .. '!folders!vRnrOJqSMlxbSgyX' @ 0 : 0; will stop at (end)

View File

@@ -1,7 +1,11 @@
2025/10/27-20:03:18.738218 7f2a23fff6c0 Recovering log #79 2025/11/09-19:53:34.224884 7f09217fa6c0 Delete type=3 #1
2025/10/27-20:03:18.749460 7f2a23fff6c0 Delete type=3 #77 2025/11/09-22:04:27.111245 7f0920bff6c0 Level-0 table #96: started
2025/10/27-20:03:18.749597 7f2a23fff6c0 Delete type=0 #79 2025/11/09-22:04:27.111285 7f0920bff6c0 Level-0 table #96: 0 bytes OK
2025/10/27-20:06:09.651196 7f2a213ff6c0 Level-0 table #84: started 2025/11/09-22:04:27.145066 7f0920bff6c0 Delete type=0 #94
2025/10/27-20:06:09.651261 7f2a213ff6c0 Level-0 table #84: 0 bytes OK 2025/11/09-22:04:27.287309 7f0920bff6c0 Manual compaction at level-0 from '!actors!0FQ6XaRi24OorI21' @ 72057594037927935 : 1 .. '!folders!vRnrOJqSMlxbSgyX' @ 0 : 0; will stop at '!folders!vRnrOJqSMlxbSgyX' @ 92 : 1
2025/10/27-20:06:09.658656 7f2a213ff6c0 Delete type=0 #82 2025/11/09-22:04:27.287329 7f0920bff6c0 Compacting 1@0 + 0@1 files
2025/10/27-20:06:09.658955 7f2a213ff6c0 Manual compaction at level-0 from '!actors!0FQ6XaRi24OorI21' @ 72057594037927935 : 1 .. '!folders!vRnrOJqSMlxbSgyX' @ 0 : 0; will stop at (end) 2025/11/09-22:04:27.306281 7f0920bff6c0 Generated table #97@0: 78 keys, 62826 bytes
2025/11/09-22:04:27.306318 7f0920bff6c0 Compacted 1@0 + 0@1 files => 62826 bytes
2025/11/09-22:04:27.339052 7f0920bff6c0 compacted to: files[ 0 1 0 0 0 0 0 ]
2025/11/09-22:04:27.339182 7f0920bff6c0 Delete type=2 #68
2025/11/09-22:04:27.437605 7f0920bff6c0 Manual compaction at level-0 from '!folders!vRnrOJqSMlxbSgyX' @ 92 : 1 .. '!folders!vRnrOJqSMlxbSgyX' @ 0 : 0; will stop at (end)

Binary file not shown.

View File

View File

@@ -214,6 +214,68 @@
padding-left: 5px; padding-left: 5px;
} }
} }
.crew {
.crew-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
padding: 4px;
}
.crew-member {
display: flex;
align-items: center;
gap: 4px;
padding: 4px;
border: 1px solid var(--color-border-dark);
border-radius: 4px;
background: var(--color-light-2);
.crew-img {
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid var(--color-border-dark);
cursor: pointer;
&:hover {
opacity: 0.8;
box-shadow: 0 0 4px var(--color-shadow-primary);
}
}
.crew-name {
flex: 1;
font-size: var(--font-size-small);
cursor: pointer;
&:hover {
color: var(--color-text-dark-highlight);
text-shadow: 0 0 4px var(--color-shadow-primary);
}
}
.controls {
display: flex;
gap: 4px;
min-width: 3rem;
a {
color: var(--color-text-dark-primary);
&:hover {
color: var(--color-text-dark-highlight);
}
}
.pilot-button {
color: var(--color-text-light-primary);
&:hover {
color: var(--color-text-light-highlight);
}
}
}
}
.crew-empty {
grid-column: 1 / -1;
text-align: center;
padding: 8px;
font-style: italic;
color: var(--color-text-dark-secondary);
}
}
.weapons { .weapons {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);

View File

@@ -173,6 +173,68 @@
padding-left: 5px; padding-left: 5px;
} }
} }
.crew {
.crew-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
padding: 4px;
}
.crew-member {
display: flex;
align-items: center;
gap: 4px;
padding: 4px;
border: 1px solid var(--color-border-dark);
border-radius: 4px;
background: var(--color-light-2);
.crew-img {
width: 32px;
height: 32px;
border-radius: 50%;
border: 1px solid var(--color-border-dark);
cursor: pointer;
&:hover {
opacity: 0.8;
box-shadow: 0 0 4px var(--color-shadow-primary);
}
}
.crew-name {
flex: 1;
font-size: var(--font-size-small);
cursor: pointer;
&:hover {
color: var(--color-text-dark-highlight);
text-shadow: 0 0 4px var(--color-shadow-primary);
}
}
.controls {
display: flex;
gap: 4px;
min-width: 3rem;
a {
color: var(--color-text-dark-primary);
&:hover {
color: var(--color-text-dark-highlight);
}
}
.pilot-button {
color: var(--color-text-light-primary);
&:hover {
color: var(--color-text-light-highlight);
}
}
}
}
.crew-empty {
grid-column: 1 / -1;
text-align: center;
padding: 8px;
font-style: italic;
color: var(--color-text-dark-secondary);
}
}
.weapons { .weapons {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);

View File

@@ -44,6 +44,12 @@
<select name="numericModifierSelect" class="roll-numeric-modifier"> <select name="numericModifierSelect" class="roll-numeric-modifier">
{{selectOptions choiceNumericModifier selected=numericModifierSelect}} {{selectOptions choiceNumericModifier selected=numericModifierSelect}}
</select> </select>
{{#if vehicleBonus}}
<div class="vehicle-bonus-info">
<label>{{localize "FTLNOMAD.Label.vehicleBonus"}}</label>
<span class="vehicle-bonus-value">+{{vehicleBonus}}</span>
</div>
{{/if}}
{{/if}} {{/if}}
{{#if (eq rollType "weapon")}} {{#if (eq rollType "weapon")}}

View File

@@ -4,63 +4,43 @@
data-group="{{tab.group}}" data-group="{{tab.group}}"
> >
<div class="main-div"> <div class="main-div">
<fieldset class="crew">
<!-- <div class="weapons"> <legend>{{localize "FTLNOMAD.Label.crew"}}
<fieldset> ({{crewMembers.length}}{{#if
<legend>{{localize "FTLNOMAD.Label.weapons"}}{{#if isEditMode}} system.crewCapacity
<a }}/{{system.crewCapacity}}{{/if}})</legend>
class="action" <div class="crew-list">
data-tooltip="{{localize ' FTLNOMAD.Tooltip.addWeapon'}}" {{#each crewMembers as |member|}}
data-tooltip-direction="UP"
><i class="fas fa-plus" data-action="createWeapon"></i></a>{{/if}}
</legend>
{{#each weapons as |item|}}
{{!log 'weapon' this}}
<div <div
class="weapon item" class="crew-member"
data-item-id="{{item.id}}" data-actor-uuid="{{member.uuid}}"
data-item-uuid="{{item.uuid}}"
data-drag="true" data-drag="true"
> >
<img <img
class="item-img" class="crew-img"
src="{{item.img}}" src="{{member.img}}"
data-tooltip="{{item.name}}" data-tooltip="{{member.name}}"
data-action="viewCrew"
/> />
<div <div class="crew-name" data-action="viewCrew">{{member.name}}</div>
class="name rollable"
data-roll-type="weapon"
data-tooltip="{{{item.system.description}}}"
>
{{item.name}}
</div>
<a
class="damage rollable"
data-item-id="{{item.id}}"
data-action="roll"
data-roll-type="damage"
data-roll-value="{{item.system.damage}}"
>{{localize "FTLNOMAD.Label.damageShort"}}
:
{{item.system.damage}}</a>
<div class="controls"> <div class="controls">
<a <a
data-tooltip="{{localize 'FTLNOMAD.Edit'}}" class="pilot-button"
data-action="edit" data-tooltip="{{localize 'FTLNOMAD.PilotStarship'}}"
data-item-id="{{item.id}}" data-action="pilotCrew"
data-item-uuid="{{item.uuid}}" ><i class="fas fa-dice-d6"></i></a>
><i class="fas fa-edit"></i></a>
<a <a
data-tooltip="{{localize 'FTLNOMAD.Delete'}}" data-tooltip="{{localize 'FTLNOMAD.RemoveCrew'}}"
data-action="delete" data-action="removeCrew"
data-item-id="{{item.id}}" data-crew-uuid="{{member.uuid}}"
data-item-uuid="{{item.uuid}}" ><i class="fas fa-times"></i></a>
><i class="fas fa-trash"></i></a>
</div> </div>
</div> </div>
{{else}}
<div class="crew-empty">{{localize "FTLNOMAD.Label.noCrew"}}</div>
{{/each}} {{/each}}
</div> </div>
</fieldset> --> </fieldset>
<fieldset> <fieldset>
<legend>{{localize "FTLNOMAD.Label.equipment"}}{{#if isEditMode}} <legend>{{localize "FTLNOMAD.Label.equipment"}}{{#if isEditMode}}

View File

@@ -96,8 +96,8 @@
<legend>{{localize "FTLNOMAD.Label.cargo"}}</legend> <legend>{{localize "FTLNOMAD.Label.cargo"}}</legend>
<div class="flexrow"> <div class="flexrow">
{{formField {{formField
systemFields.crew systemFields.crewCapacity
value=system.crew value=system.crewCapacity
localize=true localize=true
disabled=isPlayMode disabled=isPlayMode
}} }}

View File

@@ -4,6 +4,45 @@
data-group="{{tab.group}}" data-group="{{tab.group}}"
> >
<div class="main-div"> <div class="main-div">
<fieldset class="crew">
<legend>{{localize "FTLNOMAD.Label.crew"}}
({{crewMembers.length}}{{#if
system.crewCapacity
}}/{{system.crewCapacity}}{{/if}})</legend>
<div class="crew-list">
{{#each crewMembers as |member|}}
<div
class="crew-member"
data-actor-uuid="{{member.uuid}}"
data-drag="true"
>
<img
class="crew-img"
src="{{member.img}}"
data-tooltip="{{member.name}}"
data-action="viewCrew"
/>
<div class="crew-name" data-action="viewCrew">{{member.name}}</div>
<div class="controls">
<a
class="pilot-button"
data-tooltip="{{localize 'FTLNOMAD.PilotVehicle'}}"
data-action="pilotCrew"
><i class="fas fa-dice-d6"></i></a>
<a
data-tooltip="{{localize 'FTLNOMAD.RemoveCrew'}}"
data-action="removeCrew"
data-crew-uuid="{{member.uuid}}"
><i class="fas fa-times"></i></a>
</div>
</div>
{{else}}
<div class="crew-empty">{{localize "FTLNOMAD.Label.noCrew"}}</div>
{{/each}}
</div>
</fieldset>
<fieldset> <fieldset>
<legend>{{localize "FTLNOMAD.Label.weapons"}}{{#if isEditMode}} <legend>{{localize "FTLNOMAD.Label.weapons"}}{{#if isEditMode}}
<a <a

View File

@@ -81,8 +81,8 @@
<legend>{{localize "FTLNOMAD.Label.cargo"}}</legend> <legend>{{localize "FTLNOMAD.Label.cargo"}}</legend>
<div class="cargo-content"> <div class="cargo-content">
{{formField {{formField
systemFields.crew systemFields.crewCapacity
value=system.crew value=system.crewCapacity
localize=true localize=true
disabled=isPlayMode disabled=isPlayMode
}} }}
@@ -100,7 +100,6 @@
}} }}
</div> </div>
</fieldset> </fieldset>
</div> </div>
</div> </div>
</fieldset> </fieldset>