Add spells rolls and enhance CSS styling

- Add spell roll functionality to character sheets
- Enhance CSS and LESS styling for better visual presentation
- Update character templates and models
- Remove old backup files (roll-old.mjs, roll.mjs.backup)
- Improve character combat and equipment templates
- Update utility functions and actor documents

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-01-21 13:56:09 +01:00
parent 7283f5f15b
commit abea77906d
15 changed files with 519 additions and 2283 deletions
+51 -51
View File
@@ -89,6 +89,11 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.actionPoints = new fields.SchemaField({
value: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 }),
max: new fields.NumberField({ ...requiredInteger, initial: 0, min: 0 })
})
schema.biodata = new fields.SchemaField({
level: new fields.NumberField({ ...requiredInteger, initial: 1, min: 1 }),
alignment: new fields.StringField({ required: true, nullable: false, initial: "" }),
@@ -165,6 +170,21 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
prepareDerivedData() {
super.prepareDerivedData();
// Calculate action points max based on level
const level = this.biodata.level
let actionPointsMax = 4
if (level >= 3 && level <= 5) {
actionPointsMax = 5
} else if (level >= 6 && level <= 8) {
actionPointsMax = 6
} else if (level >= 9 && level <= 10) {
actionPointsMax = 7
}
// Set max action points (but don't override if already set to a higher value)
if (this.actionPoints.max < actionPointsMax) {
this.actionPoints.max = actionPointsMax
}
// Calculate sub-attributes from parent characteristics
// Sub-attribute = lowest ability modifier between the two parent characteristics
for (let subAttrKey in SYSTEM.SUB_ATTRIBUTES) {
@@ -241,66 +261,46 @@ export default class PrismRPGCharacter extends foundry.abstract.TypeDataModel {
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
/**
* Rolls initiative for the character: 1d20 + initiative modifier
* @param {string} combatId - Optional combat ID to update
* @param {string} combatantId - Optional combatant ID to update
* @returns {Promise<Roll|null>} The initiative roll or null if cancelled
*/
async rollInitiative(combatId = undefined, combatantId = undefined) {
const hasTarget = false
let actorClass = this.biodata.class;
// Get the initiative sub-attribute modifier
const initiativeModifier = this.subAttributes.initiative.value
let wisDef = SYSTEM.CHARACTERISTICS_TABLES.wis.find((c) => c.value === this.characteristics.wis.value)
let maxInit = Number(wisDef.init_cap) || 1000
// Create the roll formula: 1d20 + initiative modifier
const formula = `1d20 + ${initiativeModifier}`
let roll = await PrismRPGRoll.promptInitiative({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
combatId,
combatantId,
actorClass,
maxInit,
// Roll the initiative
let initRoll = new Roll(formula)
await initRoll.evaluate()
// Create the chat message
let msg = await initRoll.toMessage({
flavor: `${game.i18n.localize("PRISMRPG.Label.initiative")} - ${this.parent.name}`,
speaker: ChatMessage.getSpeaker({ actor: this.parent })
})
if (!roll) return null
await roll.toMessage({}, { rollMode: roll.options.rollMode })
}
async rollProgressionDice(combatId, combatantId, rollProgressionCount) {
// Get all weapons from the actor
let weapons = this.parent.items.filter(i => i.type === "weapon" && i.system.weaponType === "melee")
let weaponsChoices = weapons.map(w => { return { id: w.id, name: `${w.name} (${w.system.combatProgressionDice.toUpperCase()})`, combatProgressionDice: w.system.combatProgressionDice.toUpperCase() } })
let rangeWeapons = this.parent.items.filter(i => i.type === "weapon" && i.system.weaponType === "ranged")
for (let w of rangeWeapons) {
weaponsChoices.push({ id: `${w.id}simpleAim`, name: `${w.name} (Simple Aim: ${w.system.speed.simpleAim.toUpperCase()})`, combatProgressionDice: w.system.speed.simpleAim.toUpperCase() })
weaponsChoices.push({ id: `${w.id}carefulAim`, name: `${w.name} (Careful Aim: ${w.system.speed.carefulAim.toUpperCase()})`, combatProgressionDice: w.system.speed.carefulAim.toUpperCase() })
weaponsChoices.push({ id: `${w.id}focusedAim`, name: `${w.name} (Focused Aim: ${w.system.speed.focusedAim.toUpperCase()})`, combatProgressionDice: w.system.speed.focusedAim.toUpperCase() })
// Wait for 3D dice animation if enabled
if (game?.dice3d) {
await game.dice3d.waitFor3DAnimationByMessageID(msg.id)
}
if (this.biodata.magicUser || this.biodata.clericUser) {
let spells = this.parent.items.filter(i => i.type === "spell" || i.type === "miracle")
for (let s of spells) {
let title = ""
let formula = ""
if (s.type === "spell") {
let dice = PrismRPGUtils.getLethargyDice(s.system.level)
title = `${s.name} (Casting time: ${s.system.castingTime}, Lethargy: ${dice})`
formula = `${s.system.castingTime}+${dice}`
} else {
title = `${s.name} (Prayer time: ${s.system.prayerTime})`
formula = `${s.system.prayerTime}`
}
weaponsChoices.push({ id: s.id, name: title, combatProgressionDice: formula })
// Update the combatant's initiative if in combat
if (combatId && combatantId) {
let combat = game.combats.get(combatId)
if (combat) {
await combat.updateEmbeddedDocuments("Combatant", [{
_id: combatantId,
initiative: initRoll.total
}])
}
}
let roll = await PrismRPGRoll.promptCombatAction({
actorId: this.parent.id,
actorName: this.parent.name,
actorImage: this.parent.img,
weaponsChoices,
combatId,
combatantId,
rollProgressionCount,
type: "progression",
})
return initRoll
}
}