ENhance actor sheet with roll messages
This commit is contained in:
+158
-2
@@ -1,4 +1,5 @@
|
||||
import AwERoll from "./roll.mjs"
|
||||
import { SYSTEM } from "../config/system.mjs"
|
||||
|
||||
export default class AwEActor extends Actor {
|
||||
/** @override */
|
||||
@@ -48,11 +49,15 @@ export default class AwEActor extends Actor {
|
||||
bonus: f.system.knowledgeBonus ?? ""
|
||||
})).filter(f => f.bonus !== "") ?? []
|
||||
|
||||
return AwERoll.prompt({
|
||||
const { conditionBonus, conditionLabels } = this.#buildConditionOptions()
|
||||
|
||||
const roll = await AwERoll.prompt({
|
||||
attributeKey: attrId,
|
||||
modifier: attribute.mod ?? 0,
|
||||
attributeBonus: attribute.bonus ?? 0,
|
||||
knowledgeBonuses,
|
||||
conditionBonus,
|
||||
conditionLabels,
|
||||
actorId: this.id,
|
||||
actorName: this.name,
|
||||
actorImage: this.img,
|
||||
@@ -62,6 +67,28 @@ export default class AwEActor extends Actor {
|
||||
damageType: weaponItem.system.damageType,
|
||||
...options
|
||||
})
|
||||
|
||||
// Remove consumed conditions
|
||||
if (roll && this.statuses.has("edge")) await this.toggleStatusEffect("edge")
|
||||
return roll
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll weapon damage directly (no attack roll).
|
||||
* @param {Item} weaponItem - The weapon item.
|
||||
* @returns {Promise<Roll>}
|
||||
*/
|
||||
async rollDamage(weaponItem) {
|
||||
const formula = weaponItem.system.damageFormula
|
||||
if (!formula) return ui.notifications.warn(game.i18n.localize("AWEMMY.Weapon.NoDamageFormula"))
|
||||
const roll = new Roll(formula)
|
||||
await roll.evaluate()
|
||||
const typeStr = weaponItem.system.damageType ? ` (${weaponItem.system.damageType})` : ""
|
||||
await roll.toMessage({
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
flavor: `<strong>${weaponItem.name}</strong> — ${game.i18n.localize("AWEMMY.Weapon.DamageRoll")}${typeStr}`
|
||||
})
|
||||
return roll
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,15 +107,144 @@ export default class AwEActor extends Actor {
|
||||
bonus: f.system.knowledgeBonus ?? ""
|
||||
})).filter(f => f.bonus !== "") ?? []
|
||||
|
||||
return AwERoll.prompt({
|
||||
const { conditionBonus, conditionLabels } = this.#buildConditionOptions()
|
||||
|
||||
const roll = await AwERoll.prompt({
|
||||
attributeKey: attributeId,
|
||||
modifier: attribute.mod ?? 0,
|
||||
attributeBonus: attribute.bonus ?? 0,
|
||||
knowledgeBonuses,
|
||||
conditionBonus,
|
||||
conditionLabels,
|
||||
actorId: this.id,
|
||||
actorName: this.name,
|
||||
actorImage: this.img,
|
||||
...options
|
||||
})
|
||||
|
||||
// Remove consumed conditions
|
||||
if (roll && this.statuses.has("edge")) await this.toggleStatusEffect("edge")
|
||||
return roll
|
||||
}
|
||||
|
||||
#buildConditionOptions() {
|
||||
let conditionBonus = 0
|
||||
const conditionLabels = []
|
||||
if (this.statuses.has("edge")) {
|
||||
conditionBonus += 2
|
||||
conditionLabels.push({ label: game.i18n.localize("AWEMMY.Condition.Edge"), bonus: 2 })
|
||||
}
|
||||
if (this.statuses.has("prone")) {
|
||||
conditionBonus -= 2
|
||||
conditionLabels.push({ label: game.i18n.localize("AWEMMY.Condition.Prone"), bonus: -2 })
|
||||
}
|
||||
if (this.statuses.has("jumbled")) {
|
||||
conditionBonus -= 2
|
||||
conditionLabels.push({ label: game.i18n.localize("AWEMMY.Condition.Jumbled"), bonus: -2 })
|
||||
}
|
||||
return { conditionBonus, conditionLabels }
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a kit item: decrement charges and post a chat message.
|
||||
* @param {string} kitId - The kit item ID.
|
||||
*/
|
||||
async useKit(kitId) {
|
||||
const item = this.items.get(kitId)
|
||||
if (!item) return
|
||||
const charges = item.system.charges
|
||||
if (charges.value <= 0) {
|
||||
ui.notifications.warn(game.i18n.format("AWEMMY.Kit.Depleted", { name: item.name }))
|
||||
return
|
||||
}
|
||||
await item.update({ "system.charges.value": charges.value - 1 })
|
||||
await ChatMessage.create({
|
||||
speaker: ChatMessage.getSpeaker({ actor: this }),
|
||||
content: `<p>${game.i18n.format("AWEMMY.Kit.Used", { name: item.name, value: charges.value - 1, max: charges.max })}</p>`
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Use an ability item: check daily/FP constraints, deduct FP, mark used, post chat card.
|
||||
* @param {string} abilityId - The ability item ID.
|
||||
*/
|
||||
async useAbility(abilityId) {
|
||||
const item = this.items.get(abilityId)
|
||||
if (!item) return
|
||||
const sys = item.system
|
||||
|
||||
if (sys.usedToday) {
|
||||
ui.notifications.warn(game.i18n.format("AWEMMY.Ability.AlreadyUsed", { name: item.name }))
|
||||
return
|
||||
}
|
||||
|
||||
if (sys.flowPointCost > 0) {
|
||||
const fp = this.system.flowPoints.value
|
||||
if (fp < sys.flowPointCost) {
|
||||
ui.notifications.warn(game.i18n.format("AWEMMY.Ability.NotEnoughFP", { name: item.name, cost: sys.flowPointCost, current: fp }))
|
||||
return
|
||||
}
|
||||
await this.update({ "system.flowPoints.value": fp - sys.flowPointCost })
|
||||
}
|
||||
|
||||
const isDaily = sys.frequency?.toLowerCase().includes("day")
|
||||
if (isDaily) await item.update({ "system.usedToday": true })
|
||||
|
||||
const abilityTypeLabel = game.i18n.localize(SYSTEM.ABILITY_TYPE[sys.abilityType]?.label ?? sys.abilityType)
|
||||
const costLabel = game.i18n.localize(SYSTEM.ABILITY_COST[sys.cost]?.label ?? sys.cost)
|
||||
const enrichedDescription = await foundry.applications.ux.TextEditor.implementation.enrichHTML(sys.description ?? "", { async: true, relativeTo: item })
|
||||
const content = await foundry.applications.handlebars.renderTemplate(
|
||||
"systems/fvtt-adventures-with-emmy/templates/ability-use.hbs",
|
||||
{
|
||||
name: item.name,
|
||||
img: item.img,
|
||||
costLabel,
|
||||
abilityTypeLabel,
|
||||
traits: sys.traits ?? [],
|
||||
frequency: sys.frequency,
|
||||
trigger: sys.trigger,
|
||||
requirements: sys.requirements,
|
||||
flowPointCost: sys.flowPointCost || 0,
|
||||
description: enrichedDescription
|
||||
}
|
||||
)
|
||||
await ChatMessage.create({ speaker: ChatMessage.getSpeaker({ actor: this }), content })
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a long rest: restore HP, reset daily abilities, refill kits, post chat card.
|
||||
* No confirmation dialog — caller is responsible for confirming if needed.
|
||||
*/
|
||||
async longRest() {
|
||||
const sys = this.system
|
||||
const updates = {}
|
||||
const summary = []
|
||||
|
||||
const hpMissing = sys.hp.max - sys.hp.value
|
||||
if (hpMissing > 0) {
|
||||
updates["system.hp.value"] = sys.hp.max
|
||||
summary.push(game.i18n.format("AWEMMY.Rest.HPRestored", { amount: hpMissing, max: sys.hp.max }))
|
||||
}
|
||||
if (Object.keys(updates).length > 0) await this.update(updates)
|
||||
|
||||
const dailyAbilities = this.itemTypes.ability.filter(i => i.system.usedToday)
|
||||
if (dailyAbilities.length) {
|
||||
await this.updateEmbeddedDocuments("Item", dailyAbilities.map(i => ({ _id: i.id, "system.usedToday": false })))
|
||||
summary.push(game.i18n.format("AWEMMY.Rest.AbilitiesReset", { count: dailyAbilities.length }))
|
||||
}
|
||||
|
||||
const depleted = this.itemTypes.kit.filter(i => i.system.charges.value < i.system.charges.max)
|
||||
if (depleted.length) {
|
||||
await this.updateEmbeddedDocuments("Item", depleted.map(i => ({ _id: i.id, "system.charges.value": i.system.charges.max })))
|
||||
summary.push(game.i18n.format("AWEMMY.Rest.KitsReplenished", { count: depleted.length }))
|
||||
}
|
||||
|
||||
const bulletList = summary.map(s => `<li>${s}</li>`).join("")
|
||||
const content = `
|
||||
<div class="awemmy-rest-message">
|
||||
<h3><i class="fa-solid fa-moon"></i> ${game.i18n.format("AWEMMY.Rest.LongRestTitle", { name: this.name })}</h3>
|
||||
${summary.length ? `<ul>${bulletList}</ul>` : `<p>${game.i18n.localize("AWEMMY.Rest.AlreadyRested")}</p>`}
|
||||
</div>`
|
||||
await ChatMessage.create({ speaker: ChatMessage.getSpeaker({ actor: this }), content })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user