Add spells rolls and enhance CSS styling

This commit is contained in:
2026-01-12 10:45:20 +01:00
parent e75824cd20
commit 0bc6b43ffe
29 changed files with 2428 additions and 878 deletions
+162 -27
View File
@@ -136,30 +136,40 @@ export default class PrismRPGRoll extends Roll {
case "weapon-attack":
options.rollName = options.rollTarget.name
// Default to STR for melee, DEX for ranged (will be updated by dialog choice)
if (options.rollTarget.weapon.system.weaponType === "melee") {
options.rollTarget.value = options.rollTarget.combat.attackModifier +
options.rollTarget.weaponSkillModifier +
options.rollTarget.value = options.rollTarget.strMod +
options.rollTarget.weapon.system.bonuses.attackBonus
} else {
options.rollTarget.value = options.rollTarget.combat.rangedAttackModifier +
options.rollTarget.weaponSkillModifier +
options.rollTarget.value = options.rollTarget.dexMod +
options.rollTarget.weapon.system.bonuses.attackBonus
}
break
case "weapon-defense":
options.rollName = options.rollTarget.name
options.rollTarget.value = options.rollTarget.combat.defenseModifier +
options.rollTarget.weaponSkillModifier +
options.rollTarget.weapon.system.bonuses.defenseBonus
break
case "spell":
case "spell-attack":
case "spell-power":
case "spell-cast":
options.rollName = options.rollTarget.name
options.rollTarget.value = options.rollTarget.actorModifiers.levelSpellModifier +
options.rollTarget.actorModifiers.intSpellModifier
// Find best mental characteristic (INT, WIS, CHA)
const actor = game.actors.get(options.actorId)
const intMod = this.getAbilityModifier(actor.system.characteristics.int.value)
const wisMod = this.getAbilityModifier(actor.system.characteristics.wis.value)
const chaMod = this.getAbilityModifier(actor.system.characteristics.cha.value)
const bestMentalMod = Math.max(intMod, wisMod, chaMod)
options.rollTarget.value = bestMentalMod
// Store which characteristic is being used
if (bestMentalMod === intMod) {
options.rollTarget.mentalCharacteristic = "INT"
options.rollTarget.mentalCharValue = actor.system.characteristics.int.value
} else if (bestMentalMod === wisMod) {
options.rollTarget.mentalCharacteristic = "WIS"
options.rollTarget.mentalCharValue = actor.system.characteristics.wis.value
} else {
options.rollTarget.mentalCharacteristic = "CHA"
options.rollTarget.mentalCharValue = actor.system.characteristics.cha.value
}
break
case "miracle":
@@ -188,17 +198,18 @@ export default class PrismRPGRoll extends Roll {
if (options.rollType.includes("weapon-damage")) {
isDamageRoll = true
hasAdvantage = false
options.rollName = options.rollTarget.name
let damageBonus = options.rollTarget.combat.damageModifier
options.rollTarget.value = damageBonus +
options.rollTarget.weaponSkillModifier +
options.rollTarget.weapon.system.bonuses.damageBonus
if (options.rollType.includes("small")) {
dice = options.rollTarget.weapon.system.damage.damageS
options.rollName = options.rollTarget.weapon.name
// Default to STR for melee, DEX for ranged (will be updated by dialog choice)
if (options.rollTarget.weapon.system.weaponType === "melee") {
options.rollTarget.value = options.rollTarget.strMod +
options.rollTarget.weapon.system.bonuses.damageBonus
} else {
dice = options.rollTarget.weapon.system.damage.damageM
options.rollTarget.value = options.rollTarget.dexMod +
options.rollTarget.weapon.system.bonuses.damageBonus
}
// Use the weapon's damage dice
dice = options.rollTarget.weapon.system.damage || "1d6"
dice = dice.replace(/E/gi, "")
} else if (options.rollType.includes("monster-damage")) {
isDamageRoll = true
@@ -218,6 +229,44 @@ export default class PrismRPGRoll extends Roll {
const rollModes = foundry.utils.duplicate(CONFIG.Dice.rollModes)
const choiceModifier = SYSTEM.CHOICE_MODIFIERS
const choiceAdvantage = SYSTEM.ADVANTAGE_CHOICES
const attackerAimChoices = SYSTEM.ATTACKER_AIM_CHOICES
// For weapon damage rolls, skip dialog and roll directly
if (options.rollType.includes("weapon-damage")) {
// Just roll the weapon's damage dice, no modifiers
const finalFormula = dice
const rollData = {
type: options.rollType,
rollType: options.rollType,
target: options.rollTarget,
rollName: options.rollName,
actorId: options.actorId,
actorName: options.actorName,
actorImage: options.actorImage,
rollMode: "publicroll",
hasTarget: options.hasTarget,
titleFormula: finalFormula
}
if (Hooks.call("fvtt-prism-rpg.preRoll", options, rollData) === false) return
// Execute the roll
let roll = new this(finalFormula, options.data, rollData)
await roll.evaluate()
// Store results
const duplicatedRollTarget = foundry.utils.duplicate(options.rollTarget)
roll.options.resultType = "success"
roll.options.rollTotal = roll.total
roll.options.rollTarget = duplicatedRollTarget
roll.options.titleFormula = finalFormula
roll.options.rollData = foundry.utils.duplicate(rollData)
if (Hooks.call("fvtt-prism-rpg.Roll", options, rollData, roll) === false) return
return roll
}
let dialogContext = {
rollType: options.rollType,
@@ -232,13 +281,14 @@ export default class PrismRPGRoll extends Roll {
dice,
choiceModifier,
choiceAdvantage,
attackerAimChoices,
hasTarget: options.hasTarget,
modifier: "+0",
advantage: "none"
}
const content = await foundry.applications.handlebars.renderTemplate(
"systems/fvtt-prism-rpg/templates/roll-dialog.hbs",
"systems/fvtt-prism-rpg/templates/roll-dialog-v2.hbs",
dialogContext
)
@@ -255,7 +305,13 @@ export default class PrismRPGRoll extends Roll {
callback: (event, button, dialog) => {
game.user.setFlag(SYSTEM.id, "roll-dialog-pos", foundry.utils.duplicate(dialog.position))
const output = Array.from(button.form.elements).reduce((obj, input) => {
if (input.name) obj[input.name] = input.value
if (input.name) {
if (input.type === "checkbox") {
obj[input.name] = input.checked
} else {
obj[input.name] = input.value
}
}
return obj
}, {})
return output
@@ -272,9 +328,29 @@ export default class PrismRPGRoll extends Roll {
if (hasModifier) {
let bonus = Number(options.rollTarget.value) || 0
let extraModifier = rollContext.modifier === "" ? 0 : parseInt(rollContext.modifier, 10)
// Recalculate bonus if player chose different attribute for weapon attack/damage
if (rollContext.attackAttribute && options.rollTarget.weapon) {
const chosenMod = rollContext.attackAttribute === "str" ? options.rollTarget.strMod : options.rollTarget.dexMod
const weaponBonus = options.rollTarget.weapon.system.bonuses.attackBonus || 0
const damageBonus = options.rollTarget.weapon.system.bonuses.damageBonus || 0
if (options.rollType === "weapon-attack") {
bonus = chosenMod + weaponBonus
} else if (options.rollType.includes("weapon-damage")) {
bonus = chosenMod + damageBonus
}
}
let extraModifier = rollContext.modifier === "" ? 0 : Number.parseInt(rollContext.modifier, 10)
totalModifier = bonus + extraModifier
// Apply aiming modifier for ranged attacks
if (rollContext.attackerAim && rollContext.attackerAim !== "0") {
const aimModifier = Number.parseInt(rollContext.attackerAim, 10)
totalModifier += aimModifier
}
if (totalModifier !== 0) {
finalFormula = totalModifier > 0 ?
`${dice} + ${totalModifier}` :
@@ -289,6 +365,35 @@ export default class PrismRPGRoll extends Roll {
finalFormula = finalFormula.replace(dice, `2${dice}kl`)
}
// Special ranged weapon modifiers
if (rollContext.letItFly) {
// Let it Fly: Pure D20E (replace with 1d20 if it was modified)
finalFormula = finalFormula.replace(/2d20k[hl]/, "1d20")
}
if (rollContext.pointBlank) {
// Point Blank: Add special advantage or bonus (implement based on your rules)
// This could add advantage or a flat bonus
}
// Handle spell upcast
let upcastLevel = 0
let totalManaCost = 0
let totalAPC = 0
let manaUpkeep = 0
let mentalCharacteristic = null
let mentalCharValue = null
if (options.rollType === "spell-cast") {
upcastLevel = rollContext.upcastLevel ? Number.parseInt(rollContext.upcastLevel, 10) : 0
totalManaCost = options.rollTarget.system.manaCost + upcastLevel
totalAPC = options.rollTarget.system.apc + upcastLevel
manaUpkeep = options.rollTarget.system.manaUpkeep
// Get mental characteristic info from rollTarget
mentalCharacteristic = options.rollTarget.mentalCharacteristic
mentalCharValue = options.rollTarget.mentalCharValue
}
const rollData = {
type: options.rollType,
rollType: options.rollType,
@@ -300,19 +405,49 @@ export default class PrismRPGRoll extends Roll {
rollMode: rollContext.visibility,
hasTarget: options.hasTarget,
titleFormula: finalFormula,
upcastLevel,
totalManaCost,
totalAPC,
manaUpkeep,
mentalCharacteristic,
mentalCharValue,
...rollContext,
}
if (Hooks.call("fvtt-prism-rpg.preRoll", options, rollData) === false) return
// Handle mana spending for spell-cast
if (options.rollType === "spell-cast" && totalManaCost > 0) {
const actor = game.actors.get(options.actorId)
const currentMana = actor.system.manaPoints.value
// Check if enough mana
if (currentMana < totalManaCost) {
ui.notifications.error(
`Not enough Mana! Need ${totalManaCost}, but only have ${currentMana} Mana points.`
)
return null
}
// Spend mana
await actor.update({
"system.manaPoints.value": currentMana - totalManaCost
})
ui.notifications.info(
`Spent ${totalManaCost} Mana (${currentMana}${currentMana - totalManaCost})`
)
}
// Execute the roll
let roll = new this(finalFormula, options.data, rollData)
await roll.evaluate()
// Store results
// Store results - duplicate rollTarget to properly serialize weapon Item
const duplicatedRollTarget = foundry.utils.duplicate(options.rollTarget)
roll.options.resultType = "success"
roll.options.rollTotal = roll.total
roll.options.rollTarget = options.rollTarget
roll.options.rollTarget = duplicatedRollTarget
roll.options.titleFormula = finalFormula
roll.options.rollData = foundry.utils.duplicate(rollData)