Fix various issues with rolls and compendiums
All checks were successful
Release Creation / build (release) Successful in 57s

This commit is contained in:
2025-08-24 16:19:35 +02:00
parent 8a5b402388
commit 668da28d2c
21 changed files with 392 additions and 101 deletions

View File

@@ -12,6 +12,8 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
contentClasses: ["starship-content"],
},
actions: {
createEquipment: FTLNomadStarshipSheet.#onCreateEquipment,
createWeapon: FTLNomadStarshipSheet.#onCreateWeapon,
},
}
@@ -23,14 +25,17 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
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: "description",
/** @override */
tabGroups = {
sheet: "equipment",
}
/**
@@ -39,6 +44,7 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
*/
#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)) {
@@ -69,6 +75,13 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
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 })
@@ -78,22 +91,31 @@ export default class FTLNomadStarshipSheet extends FTLNomadActorSheet {
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) {
const rollType = $(event.currentTarget).data("roll-type")
let rollType = $(event.currentTarget).data("roll-type")
let item
let formula
let roll
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":
formula = this.actor.system.guns
// Rolll the damage
roll = new Roll(formula)
await roll.evaluate()
roll.toMessage( { flavor: `Starship ${this.actor.name} : Guns Damage` })
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)
}

View File

@@ -33,8 +33,8 @@ export default class FTLNomadVehicleSheet extends FTLNomadActorSheet {
},
}
/** @override */
tabGroups = {
/** @override */
tabGroups = {
sheet: "equipment",
}

View File

@@ -2,15 +2,15 @@ export const SYSTEM_ID = "fvtt-ftl-nomad"
export const ASCII = `
░▒▓████████▓▒░▒▓████████▓▒░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓██████████████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░
░▒▓████████▓▒░▒▓████████▓▒░▒▓█▓▒░ ░▒▓███████▓▒░ ░▒▓██████▓▒░░▒▓██████████████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓██████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓███████▓▒░
`
@@ -52,12 +52,12 @@ export const WEAPON_TYPES = {
}
export const WEAPON_RANGE = {
"handgun": { id: "handgun", label: "FTLNOMAD.Weapon.Range.Handgun", range: {close: 0, near:0, far:-2} },
"assault": { id: "assault", label: "FTLNOMAD.Weapon.Range.Assault", range: {close: -2, near:0, far:-1, distant: -2} },
"rifle": { id: "rifle", label: "FTLNOMAD.Weapon.Range.Rifle", range: {close: -3, near:0, far:0, distant: -1} },
"melee": { id: "melee", label: "FTLNOMAD.Weapon.Range.Melee", range: {close: 0} },
"heavyweapon": { id: "heavyweapon", label: "FTLNOMAD.Weapon.Range.HeavyWeapon", range: {near:-1, far:0, distant: 0} },
"thrownweapon": { id: "thrownweapon", label: "FTLNOMAD.Weapon.Range.ThrownWeapon", range: {close: 0, near:-1} }
"handgun": { id: "handgun", label: "FTLNOMAD.Weapon.Range.Handgun", range: { close: 0, near: 0, far: -2 } },
"assault": { id: "assault", label: "FTLNOMAD.Weapon.Range.Assault", range: { close: -2, near: 0, far: -1, distant: -2 } },
"rifle": { id: "rifle", label: "FTLNOMAD.Weapon.Range.Rifle", range: { close: -3, near: 0, far: 0, distant: -1 } },
"melee": { id: "melee", label: "FTLNOMAD.Weapon.Range.Melee", range: { close: 0 } },
"heavyweapon": { id: "heavyweapon", label: "FTLNOMAD.Weapon.Range.HeavyWeapon", range: { near: -1, far: 0, distant: 0 } },
"thrownweapon": { id: "thrownweapon", label: "FTLNOMAD.Weapon.Range.ThrownWeapon", range: { close: 0, near: -1 } }
}
export const ATTACK_MODIFIERS = {
@@ -73,35 +73,35 @@ export const ATTACK_MODIFIERS = {
}
export const TRIAGE_RESULTS = {
"none": { id: "none", dice:0, label: "FTLNOMAD.TriageResults.None" },
"death": { id: "death", dice:3, label: "FTLNOMAD.TriageResults.Death" },
"critical": { id: "critical", dice:4, label: "FTLNOMAD.TriageResults.Critical" },
"severe": { id: "severe", dice:7, label: "FTLNOMAD.TriageResults.Severe" },
"moderate": { id: "moderate", dice:10, label: "FTLNOMAD.TriageResults.Moderate" },
"fleshwound": { id: "fleshwound", dice:12, label: "FTLNOMAD.TriageResults.FleshWound" }
"none": { id: "none", dice: 0, label: "FTLNOMAD.TriageResults.None" },
"death": { id: "death", dice: 3, label: "FTLNOMAD.TriageResults.Death" },
"critical": { id: "critical", dice: 4, label: "FTLNOMAD.TriageResults.Critical" },
"severe": { id: "severe", dice: 7, label: "FTLNOMAD.TriageResults.Severe" },
"moderate": { id: "moderate", dice: 10, label: "FTLNOMAD.TriageResults.Moderate" },
"fleshwound": { id: "fleshwound", dice: 12, label: "FTLNOMAD.TriageResults.FleshWound" }
}
export const CREATURE_TERRAIN_TYPES = {
"cave": { id: "cave", label: "FTLNOMAD.Creature.Terrain.Cave", niche:0, size: 0 },
"coast": { id: "coast", label: "FTLNOMAD.Creature.Terrain.Coast", niche:1, size: 0 },
"desert": { id: "desert", label: "FTLNOMAD.Creature.Terrain.Desert", niche:-1, size: -1 },
"forest": { id: "forest", label: "FTLNOMAD.Creature.Terrain.Forest", niche:1, size: 1 },
"jungle": { id: "jungle", label: "FTLNOMAD.Creature.Terrain.Jungle", niche:1, size: 1 },
"mixed": { id: "mixed", label: "FTLNOMAD.Creature.Terrain.Mixed", niche:0, size: 0 },
"mountain": { id: "mountain", label: "FTLNOMAD.Creature.Terrain.Mountain", niche:-1, size: -1 },
"ocean": { id: "ocean", label: "FTLNOMAD.Creature.Terrain.Ocean", niche:-1, size: 1 },
"river": { id: "river", label: "FTLNOMAD.Creature.Terrain.River", niche:1, size: 0 },
"ruins": { id: "ruins", label: "FTLNOMAD.Creature.Terrain.Ruins", niche:0, size: 1 },
"savannah": { id: "savannah", label: "FTLNOMAD.Creature.Terrain.Savannah", niche:0, size: 1 },
"shallows": { id: "shallows", label: "FTLNOMAD.Creature.Terrain.Shallows", niche:1, size: 0 },
"swamp": { id: "swamp", label: "FTLNOMAD.Creature.Terrain.Swamp", niche:1, size: 1 }
"cave": { id: "cave", label: "FTLNOMAD.Creature.Terrain.Cave", niche: 0, size: 0 },
"coast": { id: "coast", label: "FTLNOMAD.Creature.Terrain.Coast", niche: 1, size: 0 },
"desert": { id: "desert", label: "FTLNOMAD.Creature.Terrain.Desert", niche: -1, size: -1 },
"forest": { id: "forest", label: "FTLNOMAD.Creature.Terrain.Forest", niche: 1, size: 1 },
"jungle": { id: "jungle", label: "FTLNOMAD.Creature.Terrain.Jungle", niche: 1, size: 1 },
"mixed": { id: "mixed", label: "FTLNOMAD.Creature.Terrain.Mixed", niche: 0, size: 0 },
"mountain": { id: "mountain", label: "FTLNOMAD.Creature.Terrain.Mountain", niche: -1, size: -1 },
"ocean": { id: "ocean", label: "FTLNOMAD.Creature.Terrain.Ocean", niche: -1, size: 1 },
"river": { id: "river", label: "FTLNOMAD.Creature.Terrain.River", niche: 1, size: 0 },
"ruins": { id: "ruins", label: "FTLNOMAD.Creature.Terrain.Ruins", niche: 0, size: 1 },
"savannah": { id: "savannah", label: "FTLNOMAD.Creature.Terrain.Savannah", niche: 0, size: 1 },
"shallows": { id: "shallows", label: "FTLNOMAD.Creature.Terrain.Shallows", niche: 1, size: 0 },
"swamp": { id: "swamp", label: "FTLNOMAD.Creature.Terrain.Swamp", niche: 1, size: 1 }
}
export const CREATURE_NICHES = {
"prey": { id: "prey", label: "FTLNOMAD.Creature.Niche.Prey" },
"opportunist": { id: "opportunist", label: "FTLNOMAD.Creature.Niche.Opportunist" },
"herbivore": { id: "herbivore", label: "FTLNOMAD.Creature.Niche.Herbivore" },
"predator": { id: "predator", label: "FTLNOMAD.Creature.Niche.Predator" }
"predator": { id: "predator", label: "FTLNOMAD.Creature.Niche.Predator" }
}
export const CREATURE_SIZES = {
@@ -114,11 +114,11 @@ export const CREATURE_SIZES = {
}
export const MODIFIER_CHOICES = {
"easy": { id: "easy", label: "FTLNOMAD.Label.Easy", value :"1" },
"easy": { id: "easy", label: "FTLNOMAD.Label.Easy", value: "1" },
"moderate": { id: "moderate", label: "FTLNOMAD.Label.Moderate", value: "0" },
"difficult": { id: "difficult", label: "FTLNOMAD.Label.Difficult", value: "-1" },
"formidable": { id: "formidable", label: "FTLNOMAD.Label.Formidable", value: "-2" },
"impossible": { id: "impossible", label: "FTLNOMAD.Label.Impossible", value: "-4" }
"impossible": { id: "impossible", label: "FTLNOMAD.Label.Impossible", value: "-4" }
}
export const STARSHIP_HULL = {

View File

@@ -62,7 +62,8 @@ export default class FTLNomadRoll extends Roll {
static updateFullFormula(options) {
let fullFormula
fullFormula = `${options.formula} + ${options.rollItem.value} + ${options.skillModifier}D + ${options.rangeModifier}D + ${options.numericModifier}D`
let mod = options.rollItem?.value || 0
fullFormula = `${options.formula} + ${options.skillModifier}D + ${mod} + ${options.rangeModifier}D + ${options.numericModifier}D`
// Replace all the "+ -" with "-"
fullFormula = fullFormula.replace(/\+\s*-/g, "- ")
$('#roll-dialog-full-formula').text(fullFormula)
@@ -85,20 +86,15 @@ export default class FTLNomadRoll extends Roll {
*/
static async prompt(options = {}) {
let formula = "2d6"
let actor = game.actors.get(options.actorId)
switch (options.rollType) {
case "skill":
break
case "damage":
let formula = options.rollItem.system.damage
let damageRoll = new Roll(formula)
await damageRoll.evaluate()
await damageRoll.toMessage({
flavor: `${options.rollItem.name} - Damage Roll`
});
return
options.weapon = foundry.utils.duplicate(options.rollItem)
break
case "weapon":
let actor = game.actors.get(options.actorId)
options.weapon = foundry.utils.duplicate(options.rollItem)
options.rollItem = actor.system.skills.combat
break
@@ -106,6 +102,7 @@ export default class FTLNomadRoll extends Roll {
break
}
options.actor = actor
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
const fieldRollMode = new foundry.data.fields.StringField({
choices: rollModes,
@@ -116,7 +113,7 @@ export default class FTLNomadRoll extends Roll {
const choiceModifier = SYSTEM.MODIFIER_CHOICES
let choiceRangeModifier = {}
let rangeModifier = 0
if ( options.weapon) {
if (options.weapon) {
// Build the range modifiers
let range = SYSTEM.WEAPON_RANGE[options.weapon.system.rangeType]
for (let [key, value] of Object.entries(range.range)) {
@@ -192,7 +189,7 @@ export default class FTLNomadRoll extends Roll {
$(".select-combat-option").change(event => {
let field = $(event.target).data("field")
let modifier = SYSTEM.ATTACK_MODIFIERS[field]
if ( event.target.checked) {
if (event.target.checked) {
options.numericModifier += modifier
} else {
options.numericModifier -= modifier
@@ -212,13 +209,20 @@ export default class FTLNomadRoll extends Roll {
if (Hooks.call("fvtt-ftl-nomad.preRoll", options, rollData) === false) return
let diceFormula = `${2+Math.abs(options.numericModifier)}D6`
if ( options.numericModifier > 0 ) {
diceFormula += `kh2 + ${options.rollItem.value}`
options.numericModifier = Number(rollData.numericModifier) || 0
options.skillModifier = Number(rollData.skillModifier) || 0
options.rangeModifier = Number(rollData.rangeModifier) || 0
let mod = options.rollItem?.value || 0
// Build the dice formula
let diceFormula = `${2 + Math.abs(options.skillModifier)}D6`
if (options.skillModifier > 0) {
diceFormula += `kh2 + ${mod}`
} else {
diceFormula += `kl2 + ${options.rollItem.value}`
diceFormula += `kl2 + ${mod}`
}
console.log("FTLNomadRoll | Rolling ", diceFormula, options, rollData)
const roll = new this(diceFormula, options.data, rollData)
await roll.evaluate()
@@ -254,6 +258,8 @@ export default class FTLNomadRoll extends Roll {
*/
static createTitle(type, target) {
switch (type) {
case "damage":
return `${game.i18n.localize("FTLNOMAD.Label.titleDamage")}`
case "skill":
return `${game.i18n.localize("FTLNOMAD.Label.titleSkill")}`
case "weapon":

View File

@@ -33,4 +33,24 @@ export default class FTLNomadStarship extends foundry.abstract.TypeDataModel {
return false
}
async roll(rollType, rollItem) {
let opponentTarget
const hasTarget = opponentTarget !== undefined
let roll = await FTLNomadRoll.prompt({
rollType,
rollItem,
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
isEncumbered: false,
hasTarget,
target: opponentTarget
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
}

View File

@@ -33,23 +33,23 @@ export default class FTLNomadVehicle extends foundry.abstract.TypeDataModel {
return false
}
async roll(rollType, rollItem) {
let opponentTarget
const hasTarget = opponentTarget !== undefined
async roll(rollType, rollItem) {
let opponentTarget
const hasTarget = opponentTarget !== undefined
let roll = await FTLNomadRoll.prompt({
rollType,
rollItem,
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
isEncumbered: this.isEncumbered(),
hasTarget,
target: opponentTarget
})
if (!roll) return null
let roll = await FTLNomadRoll.prompt({
rollType,
rollItem,
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
isEncumbered: this.isEncumbered(),
hasTarget,
target: opponentTarget
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
}

View File

@@ -675,7 +675,7 @@ export default class FTLNomadUtils {
return [];
});
console.log("Melee Weapons Data", meleeWeaponsData);
console.log("Melee Weapons Data", meleeWeaponsData);
// Import each melee weapon
for (const weapon of meleeWeaponsData) {
// Check if the weapon already exists
@@ -705,4 +705,51 @@ export default class FTLNomadUtils {
}
}
static async importVehicleWeapons() {
// Create a melee weapons folder if it doesn't exist
const meleeWeaponsFolder = game.folders.getName("Vehicle Weapons") || await Folder.create({
name: "Vehicle Weapons", type: "Item"
})
if (!meleeWeaponsFolder) {
console.error("Failed to create Vehicle Weapons folder");
return;
}
// Load the melee weapons JSON file
const meleeWeaponsData = await fetch("systems/fvtt-ftl-nomad/assets/json_data/vehicleweapons.json")
.then(response => response.json())
.catch(error => {
console.error("Failed to load vehicle weapons data:", error);
return [];
});
console.log("vehicle Weapons Data", meleeWeaponsData);
// Import each melee weapon
for (const weapon of meleeWeaponsData) {
// Check if the weapon already exists
const existingWeapon = game.items.find(i => i.name === weapon.name && i.type === "weapon");
if (existingWeapon) {
console.warn(`Weapon ${weapon.name} already exists, skipping import.`);
}
// Create the weapon item
await Item.create({
name: weapon.name,
type: "weapon",
img: "systems/fvtt-ftl-nomad/assets/icons/icon_weapon.svg",
system: {
description: weapon.description,
damage: weapon.damage,
techAge: this.getTechAgeKeyFromLabel(weapon.tech_age),
weaponType: "vehicle", //SYSTEM.WEAPON_TYPES.melee.id,
rangeType: SYSTEM.WEAPON_RANGE.melee.id,
enc: 0,
aspect: weapon.aspects,
cost: weapon.cost || 0,
ammoCost: weapon.ammo_cost || 0,
magazine: weapon.mag || 1,
},
folder: meleeWeaponsFolder.id
});
}
}
}