ENhance actor sheet with roll messages
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
import { SYSTEM } from '../../config/system.mjs'
|
||||
|
||||
export let ActionHandler = null
|
||||
|
||||
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
||||
ActionHandler = class ActionHandler extends coreModule.api.ActionHandler {
|
||||
|
||||
/** @override */
|
||||
async buildSystemActions(groupIds) {
|
||||
this.actors = (!this.actor) ? this._getActors() : [this.actor]
|
||||
this.actorType = this.actor?.type
|
||||
|
||||
if (this.actorType === 'character') {
|
||||
await this.#buildCharacterActions()
|
||||
} else if (this.actorType === 'creature') {
|
||||
await this.#buildCreatureActions()
|
||||
}
|
||||
}
|
||||
|
||||
async #buildCharacterActions() {
|
||||
await this.#buildAttributes()
|
||||
await this.#buildHP()
|
||||
await this.#buildFlow()
|
||||
await this.#buildWeapons()
|
||||
await this.#buildConditions()
|
||||
await this.#buildAbilities()
|
||||
await this.#buildKits()
|
||||
await this.#buildUtility()
|
||||
}
|
||||
|
||||
async #buildCreatureActions() {
|
||||
await this.#buildAttributes()
|
||||
await this.#buildHP()
|
||||
}
|
||||
|
||||
async #buildAttributes() {
|
||||
const actions = []
|
||||
const attrKeys = ['agility', 'fitness', 'awareness', 'influence']
|
||||
const labelKeys = {
|
||||
agility: 'AWEMMY.Attribute.Agility',
|
||||
fitness: 'AWEMMY.Attribute.Fitness',
|
||||
awareness: 'AWEMMY.Attribute.Awareness',
|
||||
influence: 'AWEMMY.Attribute.Influence'
|
||||
}
|
||||
for (const key of attrKeys) {
|
||||
const attr = this.actor.system.attributes?.[key]
|
||||
if (!attr) continue
|
||||
const mod = attr.mod ?? 0
|
||||
const modText = mod >= 0 ? `+${mod}` : `${mod}`
|
||||
actions.push({
|
||||
name: coreModule.api.Utils.i18n(labelKeys[key]),
|
||||
id: key,
|
||||
info1: { text: modText },
|
||||
encodedValue: ['attribute', key].join(this.delimiter)
|
||||
})
|
||||
}
|
||||
await this.addActions(actions, { id: 'attributes', type: 'system' })
|
||||
}
|
||||
|
||||
async #buildHP() {
|
||||
const hp = this.actor.system.hp
|
||||
if (!hp) return
|
||||
const tooltip = { content: `${hp.value} / ${hp.max}`, direction: 'LEFT' }
|
||||
const actions = [
|
||||
{
|
||||
name: `${hp.value} / ${hp.max}`,
|
||||
id: 'hp_display',
|
||||
tooltip,
|
||||
encodedValue: ['hp', 'display'].join(this.delimiter)
|
||||
},
|
||||
{
|
||||
name: '+',
|
||||
id: 'hp_add',
|
||||
tooltip,
|
||||
encodedValue: ['hp', 'add'].join(this.delimiter)
|
||||
},
|
||||
{
|
||||
name: '−',
|
||||
id: 'hp_sub',
|
||||
tooltip,
|
||||
encodedValue: ['hp', 'sub'].join(this.delimiter)
|
||||
}
|
||||
]
|
||||
await this.addActions(actions, { id: 'hp', type: 'system' })
|
||||
}
|
||||
|
||||
async #buildFlow() {
|
||||
const fp = this.actor.system.flowPoints
|
||||
if (fp === undefined) return
|
||||
const tooltip = { content: `FP: ${fp.value}`, direction: 'LEFT' }
|
||||
const actions = [
|
||||
{
|
||||
name: `${fp.value} FP`,
|
||||
id: 'flow_display',
|
||||
tooltip,
|
||||
encodedValue: ['flow', 'display'].join(this.delimiter)
|
||||
},
|
||||
{
|
||||
name: '+',
|
||||
id: 'flow_add',
|
||||
tooltip,
|
||||
encodedValue: ['flow', 'add'].join(this.delimiter)
|
||||
},
|
||||
{
|
||||
name: '−',
|
||||
id: 'flow_sub',
|
||||
tooltip,
|
||||
encodedValue: ['flow', 'sub'].join(this.delimiter)
|
||||
}
|
||||
]
|
||||
await this.addActions(actions, { id: 'flow', type: 'system' })
|
||||
}
|
||||
|
||||
async #buildWeapons() {
|
||||
const weapons = this.actor.itemTypes?.weapon ?? []
|
||||
for (const weapon of weapons) {
|
||||
const attrId = weapon.system.attackAttribute
|
||||
const attr = this.actor.system.attributes?.[attrId]
|
||||
const mod = attr?.mod ?? 0
|
||||
const modText = mod >= 0 ? `+${mod}` : `${mod}`
|
||||
|
||||
const groupData = { id: `weapon_${weapon.id}`, name: weapon.name, type: 'system' }
|
||||
this.addGroup(groupData, { id: 'weapons', type: 'system' }, true)
|
||||
|
||||
const actions = [{
|
||||
name: weapon.name,
|
||||
id: `weapon_${weapon.id}`,
|
||||
info1: { text: modText },
|
||||
encodedValue: ['weapon', weapon.id].join(this.delimiter)
|
||||
}]
|
||||
await this.addActions(actions, { id: `weapon_${weapon.id}`, type: 'system' })
|
||||
}
|
||||
}
|
||||
|
||||
async #buildConditions() {
|
||||
const actions = []
|
||||
for (const [key, cond] of Object.entries(SYSTEM.CONDITIONS)) {
|
||||
const isActive = this.actor.statuses?.has(key) ?? false
|
||||
actions.push({
|
||||
name: coreModule.api.Utils.i18n(cond.label),
|
||||
id: key,
|
||||
info1: { text: isActive ? '✓' : '' },
|
||||
encodedValue: ['condition', key].join(this.delimiter)
|
||||
})
|
||||
}
|
||||
await this.addActions(actions, { id: 'conditions', type: 'system' })
|
||||
}
|
||||
|
||||
async #buildAbilities() {
|
||||
const abilities = this.actor.itemTypes?.ability ?? []
|
||||
const actions = []
|
||||
for (const ability of abilities) {
|
||||
const sys = ability.system
|
||||
const costLabel = game.i18n.localize(SYSTEM.ABILITY_COST[sys.cost]?.label ?? sys.cost)
|
||||
const isUsed = sys.usedToday
|
||||
actions.push({
|
||||
name: ability.name,
|
||||
id: ability.id,
|
||||
info1: { text: isUsed ? `${costLabel} ✓` : costLabel },
|
||||
encodedValue: ['ability', ability.id].join(this.delimiter)
|
||||
})
|
||||
}
|
||||
await this.addActions(actions, { id: 'abilities', type: 'system' })
|
||||
}
|
||||
|
||||
async #buildKits() {
|
||||
const kits = this.actor.itemTypes?.kit ?? []
|
||||
const actions = []
|
||||
for (const kit of kits) {
|
||||
const charges = kit.system.charges
|
||||
const chargesText = `${charges.value}/${charges.max}`
|
||||
actions.push({
|
||||
name: kit.name,
|
||||
id: kit.id,
|
||||
info1: { text: chargesText },
|
||||
encodedValue: ['kit', kit.id].join(this.delimiter)
|
||||
})
|
||||
}
|
||||
await this.addActions(actions, { id: 'kits', type: 'system' })
|
||||
}
|
||||
|
||||
async #buildUtility() {
|
||||
const actions = []
|
||||
actions.push({
|
||||
name: coreModule.api.Utils.i18n('AWEMMY.TAH.LongRest'),
|
||||
id: 'longRest',
|
||||
encodedValue: ['utility', 'longRest'].join(this.delimiter)
|
||||
})
|
||||
if (game.combat?.current?.tokenId === this.token?.id) {
|
||||
actions.push({
|
||||
name: coreModule.api.Utils.i18n('AWEMMY.TAH.EndTurn'),
|
||||
id: 'endTurn',
|
||||
encodedValue: ['utility', 'endTurn'].join(this.delimiter)
|
||||
})
|
||||
}
|
||||
await this.addActions(actions, { id: 'utility', type: 'system' })
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
export const MODULE = { ID: 'token-action-hud-adventures-with-emmy' }
|
||||
export const CORE_MODULE = { ID: 'token-action-hud-core' }
|
||||
export const REQUIRED_CORE_MODULE_VERSION = '2.0'
|
||||
|
||||
export const GROUP = {
|
||||
attributes: { id: 'attributes', name: 'AWEMMY.Character.Attributes', type: 'system' },
|
||||
hp: { id: 'hp', name: 'AWEMMY.TAH.HP', type: 'system' },
|
||||
flow: { id: 'flow', name: 'AWEMMY.TAH.Flow', type: 'system' },
|
||||
weapons: { id: 'weapons', name: 'AWEMMY.TAH.Weapons', type: 'system' },
|
||||
conditions: { id: 'conditions', name: 'AWEMMY.Condition.Panel', type: 'system' },
|
||||
abilities: { id: 'abilities', name: 'AWEMMY.TAH.Abilities', type: 'system' },
|
||||
kits: { id: 'kits', name: 'AWEMMY.TAH.Kits', type: 'system' },
|
||||
utility: { id: 'utility', name: 'AWEMMY.TAH.Utility', type: 'system' }
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { GROUP } from './constants.js'
|
||||
|
||||
export let DEFAULTS = null
|
||||
|
||||
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
||||
const groups = foundry.utils.deepClone(GROUP)
|
||||
Object.values(groups).forEach(group => {
|
||||
group.name = coreModule.api.Utils.i18n(group.name)
|
||||
group.listName = `Group: ${coreModule.api.Utils.i18n(group.listName ?? group.name)}`
|
||||
})
|
||||
const groupsArray = Object.values(groups)
|
||||
|
||||
DEFAULTS = {
|
||||
layout: [
|
||||
{
|
||||
nestId: 'stats',
|
||||
id: 'stats',
|
||||
name: game.i18n.localize('AWEMMY.TAH.Stats'),
|
||||
groups: [
|
||||
{ ...groups.attributes, nestId: 'stats_attributes' },
|
||||
{ ...groups.hp, nestId: 'stats_hp' },
|
||||
{ ...groups.flow, nestId: 'stats_flow' }
|
||||
]
|
||||
},
|
||||
{
|
||||
nestId: 'combat',
|
||||
id: 'combat',
|
||||
name: game.i18n.localize('AWEMMY.TAH.Combat'),
|
||||
groups: [
|
||||
{ ...groups.weapons, nestId: 'combat_weapons' },
|
||||
{ ...groups.conditions, nestId: 'combat_conditions' }
|
||||
]
|
||||
},
|
||||
{
|
||||
nestId: 'items',
|
||||
id: 'items',
|
||||
name: game.i18n.localize('AWEMMY.TAH.Items'),
|
||||
groups: [
|
||||
{ ...groups.abilities, nestId: 'items_abilities' },
|
||||
{ ...groups.kits, nestId: 'items_kits' }
|
||||
]
|
||||
},
|
||||
{
|
||||
nestId: 'utility',
|
||||
id: 'utility',
|
||||
name: game.i18n.localize('AWEMMY.TAH.Utility'),
|
||||
groups: [
|
||||
{ ...groups.utility, nestId: 'utility_utility' }
|
||||
]
|
||||
}
|
||||
],
|
||||
groups: groupsArray
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,92 @@
|
||||
export let RollHandler = null
|
||||
|
||||
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
||||
RollHandler = class RollHandler extends coreModule.api.RollHandler {
|
||||
|
||||
/** @override */
|
||||
async handleActionClick(event, encodedValue) {
|
||||
const [actionTypeId, actionId] = encodedValue.split(this.delimiter ?? '|')
|
||||
|
||||
if (this.actor) {
|
||||
await this.#handleAction(event, this.actor, this.token, actionTypeId, actionId)
|
||||
return
|
||||
}
|
||||
|
||||
const knownTypes = ['character', 'creature']
|
||||
for (const token of canvas.tokens.controlled.filter(t => knownTypes.includes(t.actor?.type))) {
|
||||
await this.#handleAction(event, token.actor, token, actionTypeId, actionId)
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async handleActionHover(event, encodedValue) {}
|
||||
|
||||
/** @override */
|
||||
async handleGroupClick(event, group) {}
|
||||
|
||||
async #handleAction(event, actor, token, actionTypeId, actionId) {
|
||||
switch (actionTypeId) {
|
||||
case 'attribute':
|
||||
await actor.rollAttribute(actionId)
|
||||
break
|
||||
case 'hp':
|
||||
await this.#handleHP(actor, actionId)
|
||||
break
|
||||
case 'flow':
|
||||
await this.#handleFlow(actor, actionId)
|
||||
break
|
||||
case 'weapon':
|
||||
await this.#handleWeapon(actor, actionId)
|
||||
break
|
||||
case 'condition':
|
||||
await actor.toggleStatusEffect(actionId)
|
||||
break
|
||||
case 'ability':
|
||||
await actor.useAbility(actionId)
|
||||
break
|
||||
case 'kit':
|
||||
await actor.useKit(actionId)
|
||||
break
|
||||
case 'utility':
|
||||
await this.#handleUtility(actor, token, actionId)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
async #handleHP(actor, actionId) {
|
||||
if (actionId === 'display') return
|
||||
const hp = actor.system.hp
|
||||
if (!hp) return
|
||||
const newValue = actionId === 'add' ? hp.value + 1 : hp.value - 1
|
||||
if (newValue < 0 || newValue > hp.max) return
|
||||
await actor.update({ 'system.hp.value': newValue })
|
||||
}
|
||||
|
||||
async #handleFlow(actor, actionId) {
|
||||
if (actionId === 'display') return
|
||||
const fp = actor.system.flowPoints
|
||||
if (fp === undefined) return
|
||||
const newValue = actionId === 'add' ? fp.value + 1 : Math.max(0, fp.value - 1)
|
||||
await actor.update({ 'system.flowPoints.value': newValue })
|
||||
}
|
||||
|
||||
async #handleWeapon(actor, actionId) {
|
||||
const weapon = actor.items.get(actionId)
|
||||
if (!weapon) return
|
||||
await actor.rollWeapon(weapon)
|
||||
}
|
||||
|
||||
async #handleUtility(actor, token, actionId) {
|
||||
switch (actionId) {
|
||||
case 'longRest':
|
||||
await actor.longRest()
|
||||
break
|
||||
case 'endTurn':
|
||||
if (game.combat?.current?.tokenId === token?.id) {
|
||||
await game.combat.nextTurn()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Register module settings.
|
||||
* Called by Token Action HUD Core to register Token Action HUD system module settings.
|
||||
* @param {function} coreUpdate Token Action HUD Core update function
|
||||
*/
|
||||
export function register(coreUpdate) {
|
||||
// No system-specific settings for now
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { ActionHandler } from './action-handler.js'
|
||||
import { RollHandler as Core } from './roll-handler.js'
|
||||
import { MODULE } from './constants.js'
|
||||
import { DEFAULTS } from './defaults.js'
|
||||
import * as systemSettings from './settings.js'
|
||||
|
||||
export let SystemManager = null
|
||||
|
||||
Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
|
||||
SystemManager = class SystemManager extends coreModule.api.SystemManager {
|
||||
|
||||
/** @override */
|
||||
getActionHandler() {
|
||||
return new ActionHandler()
|
||||
}
|
||||
|
||||
/** @override */
|
||||
getAvailableRollHandlers() {
|
||||
return { core: 'Adventures with Emmy' }
|
||||
}
|
||||
|
||||
/** @override */
|
||||
getRollHandler(rollHandlerId) {
|
||||
switch (rollHandlerId) {
|
||||
case 'core':
|
||||
default:
|
||||
return new Core()
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async registerDefaults() {
|
||||
return DEFAULTS
|
||||
}
|
||||
|
||||
/** @override */
|
||||
registerSettings(coreUpdate) {
|
||||
systemSettings.register(coreUpdate)
|
||||
}
|
||||
|
||||
/** @override */
|
||||
registerStyles() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user