Add spells rolls and enhance CSS styling
This commit is contained in:
+162
-27
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user