diff --git a/languages/br.json b/languages/br.json
index 1e88b72..e6c9aa3 100644
--- a/languages/br.json
+++ b/languages/br.json
@@ -18,8 +18,10 @@
"bonusActions": "Ações de bônus",
"cantrips": "Truques",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "Testes",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "Condições",
"crewActions": "Ações da tripulação",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "Covil",
"lairActions": "Ações de covil",
@@ -50,6 +53,7 @@
"runes": "Runes",
"saves": "Salvaguarda",
"superiorHuntersDefense": "Superior Hunter's Defense",
+ "skill": "Skill",
"skills": "Pericias",
"settings": {
"abbreviateSkills": {
diff --git a/languages/cn.json b/languages/cn.json
index 52abfe2..5db7ef3 100644
--- a/languages/cn.json
+++ b/languages/cn.json
@@ -18,8 +18,10 @@
"bonusActions": "獎勵行動",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "检定",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "状态",
"crewActions": "船員行動",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "巢穴",
"lairActions": "巢穴行動",
@@ -48,6 +51,7 @@
"rests": "休息",
"rollInitiative": "先攻掷骰",
"runes": "Runes",
+ "skill": "Skill",
"skills": "技能",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/de.json b/languages/de.json
index a2dd45a..5d168f9 100644
--- a/languages/de.json
+++ b/languages/de.json
@@ -18,8 +18,10 @@
"bonusActions": "Bonus Actions",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "Checks",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "Conditions",
"crewActions": "Crew Actions",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "Lair",
"lairActions": "Lair Actions",
@@ -48,6 +51,7 @@
"rests": "Rests",
"rollInitiative": "Initiativewurf",
"runes": "Runes",
+ "skill": "Skill",
"skills": "Skills",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/en.json b/languages/en.json
index e0d91a0..5023779 100644
--- a/languages/en.json
+++ b/languages/en.json
@@ -18,8 +18,10 @@
"bonusActions": "Bonus Actions",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "Checks",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "Conditions",
"crewActions": "Crew Actions",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "Lair",
"lairActions": "Lair Actions",
@@ -48,6 +51,7 @@
"rests": "Rests",
"rollInitiative": "Roll Initiative",
"runes": "Runes",
+ "skill": "Skill",
"skills": "Skills",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/es.json b/languages/es.json
index 830d48e..a1eaac1 100644
--- a/languages/es.json
+++ b/languages/es.json
@@ -18,8 +18,10 @@
"bonusActions": "Acciones de bonificación",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "Pruebas",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "Estados",
"crewActions": "Acciones de la tripulación",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "Guarida",
"lairActions": "Acciones de guarida",
@@ -48,6 +51,7 @@
"rests": "Descansos",
"rollInitiative": "Tirar iniciativa",
"runes": "Runes",
+ "skill": "Skill",
"skills": "Habilidades",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/fr.json b/languages/fr.json
index c3f8248..afd2da6 100644
--- a/languages/fr.json
+++ b/languages/fr.json
@@ -18,8 +18,10 @@
"bonusActions": "Actions bonus",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "Tests",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "Conditions",
"crewActions": "Actions de l'équipage",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "Antre",
"lairActions": "Actions de antre",
@@ -48,6 +51,7 @@
"rests": "Repos",
"rollInitiative": "Lancer l'initiative",
"runes": "Runes",
+ "skill": "Skill",
"skills": "Compétences",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/it.json b/languages/it.json
index 5a3bc7c..e7b8916 100644
--- a/languages/it.json
+++ b/languages/it.json
@@ -18,8 +18,10 @@
"bonusActions": "Azioni bonus",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "Caratteristiche",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "Condizioni",
"crewActions": "Azioni dell'equipaggio",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "Tana",
"lairActions": "Azioni di Tana",
@@ -48,6 +51,7 @@
"rests": "Riposi",
"rollInitiative": "Tira Iniziativa",
"runes": "Runes",
+ "skill": "Skill",
"skills": "Abilità",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/ja.json b/languages/ja.json
index 5ac541d..19f3337 100644
--- a/languages/ja.json
+++ b/languages/ja.json
@@ -18,8 +18,10 @@
"bonusActions": "ボーナス・アクション",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "判定",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "状態",
"crewActions": "乗組員の行動",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "住処",
"lairActions": "住処アクション",
@@ -48,6 +51,7 @@
"rests": "休憩",
"rollInitiative": "イニシアチブロール",
"runes": "Runes",
+ "skill": "Skill",
"skills": "技能",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/ko.json b/languages/ko.json
index 54cd1bd..f1795b5 100644
--- a/languages/ko.json
+++ b/languages/ko.json
@@ -18,8 +18,10 @@
"bonusActions": "보너스 액션",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "판정",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "상태",
"crewActions": "승무원 조치",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "소굴",
"lairActions": "소굴 액션",
@@ -48,6 +51,7 @@
"rests": "휴식",
"rollInitiative": "우선권 굴림",
"runes": "Runes",
+ "skill": "Skill",
"skills": "기술",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/languages/pl.json b/languages/pl.json
index 1567172..2745f03 100644
--- a/languages/pl.json
+++ b/languages/pl.json
@@ -18,8 +18,10 @@
"bonusActions": "Akcje bonusowe",
"cantrips": "Cantrips",
"channelDivinity": "Channel Divinity",
+ "check": "Check",
"checks": "Sprawdzenie",
"classFeatures": "Class Features",
+ "condition": "Condition",
"conditions": "Stany",
"crewActions": "Akcje załogi",
"defensiveTactics": "Defensive Tactics",
@@ -29,6 +31,7 @@
"fightingStyles": "Fighting Styles",
"huntersPrey": "Hunter's Prey",
"innateSpells": "Innate Spells",
+ "item": "Item",
"kiAbilities": "Ki Abilities",
"lair": "Matecznik",
"lairActions": "Matecznik akcje",
@@ -48,6 +51,7 @@
"rests": "Odpoczynki",
"rollInitiative": "Rzut na Inicjatywę",
"runes": "Runes",
+ "skill": "Skill",
"skills": "Umiejętności",
"superiorHuntersDefense": "Superior Hunter's Defense",
"settings": {
diff --git a/module.json b/module.json
index d45896c..855fdb0 100644
--- a/module.json
+++ b/module.json
@@ -89,7 +89,7 @@
"compatibility": [
{
"minimum": "2.1.0",
- "verified": "2.1.4"
+ "verified": "2.1.5"
}
]
}
@@ -100,9 +100,9 @@
"type": "module",
"compatibility": [
{
- "minimum": "1.2.0",
- "maximum": "1.2",
- "verified": "1.2.0"
+ "minimum": "1.3.0",
+ "maximum": "1.3",
+ "verified": "1.3.0"
}
]
}
diff --git a/scripts/action-handler.js b/scripts/action-handler.js
index d65be26..a7b3f48 100644
--- a/scripts/action-handler.js
+++ b/scripts/action-handler.js
@@ -1,142 +1,101 @@
// System Module Imports
-import { ACTIVATION_TYPE_ICON, PREPARED_ICON, PROFICIENCY_LEVEL_ICON } from './constants.js'
+import { ACTIVATION_TYPE_ICON, ACTION_TYPE, PREPARED_ICON, PROFICIENCY_LEVEL_ICON, SUBCATEGORY } from './constants.js'
import { Utils } from './utils.js'
-// Core Module Imports
-import { CoreActionHandler, CoreUtils, Logger } from './config.js'
+export let ActionHandler = null
-export class ActionHandler extends CoreActionHandler {
+Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
+ ActionHandler = class ActionHandler extends coreModule.api.ActionHandler {
// Initialize actor and token variables
- actor = null
- actors = null
- actorId = null
- actorType = null
- character = null
- token = null
- tokenId = null
-
- // Initialize items variable
- items = null
-
- // Initialize setting variables
- abbreviateSkills = null
- displaySpellInfo = null
- showItemsWithoutActivationCosts = null
- showUnchargedItems = null
- showUnequippedItems = null
- showUnpreparedSpells = null
-
- // Initialize subcategoryIds variables
- subcategoryIds = null
- activationSubcategoryIds = null
- effectSubcategoryIds = null
- featureSubcategoryIds = null
- inventorySubcategoryIds = null
- spellSubcategoryIds = null
-
- // Initialize action variables
- featureActions = null
- inventoryActions = null
- spellActions = null
-
- /**
- * Build System Actions
- * @override
- * @param {object} actionList
- * @param {object} character
- * @param {array} subcategoryIds
- * @returns {object}
- */
- async buildSystemActions (character, subcategoryIds) {
+ actors = null
+ tokens = null
+ actorType = null
+
+ // Initialize items variable
+ items = null
+
+ // Initialize setting variables
+ abbreviateSkills = null
+ displaySpellInfo = null
+ showItemsWithoutActivationCosts = null
+ showUnchargedItems = null
+ showUnequippedItems = null
+ showUnpreparedSpells = null
+
+ // Initialize subcategoryIds variables
+ activationSubcategoryIds = null
+ featureSubcategoryIds = null
+ inventorySubcategoryIds = null
+ spellSubcategoryIds = null
+
+ // Initialize action variables
+ featureActions = null
+ inventoryActions = null
+ spellActions = null
+
+ /**
+ * Build System Actions
+ * @override
+ * @param {array} subcategoryIds
+ * @returns {object}
+ */
+ async buildSystemActions (subcategoryIds) {
// Set actor and token variables
- this.actor = character?.actor
- this.actorId = this.actor?.id ?? 'multi'
- this.actors = (this.actorId === 'multi') ? this._getActors() : [this.actor]
- this.actorType = this.actor?.type
- this.token = character?.token
- this.tokenId = this.token?.id ?? 'multi'
-
- // Set items variable
- if (this.actorId !== 'multi') {
- let items = this.actor.items
- items = this._discardSlowItems(items)
- items = this.sortItemsByName(items)
- this.items = items
- }
+ this.actors = (!this.actor) ? this._getActors() : [this.actor]
+ this.tokens = (!this.token) ? this._getTokens() : [this.token]
+ this.actorType = this.actor?.type
+
+ // Set items variable
+ if (this.actor) {
+ let items = this.actor.items
+ items = this._discardSlowItems(items)
+ items = this.sortItemsByName(items)
+ this.items = items
+ }
+
+ // Set settings variables
+ this.abbreviateSkills = Utils.getSetting('abbreviateSkills')
+ this.displaySpellInfo = Utils.getSetting('displaySpellInfo')
+ this.showItemsWithoutActivationCosts = Utils.getSetting('showItemsWithoutActivationCosts')
+ this.showUnchargedItems = Utils.getSetting('showUnchargedItems')
+ this.showUnequippedItems = Utils.getSetting('showUnequippedItems')
+ this.showUnpreparedSpells = Utils.getSetting('showUnpreparedSpells')
+
+ this.activationSubcategoryIds = [
+ 'actions',
+ 'bonus-actions',
+ 'crew-actions',
+ 'lair-actions',
+ 'legendary-actions',
+ 'reactions',
+ 'other-actions'
+ ]
- // Set settings variables
- this.abbreviateSkills = Utils.getSetting('abbreviateSkills')
- this.displaySpellInfo = Utils.getSetting('displaySpellInfo')
- this.showItemsWithoutActivationCosts = Utils.getSetting('showItemsWithoutActivationCosts')
- this.showUnchargedItems = Utils.getSetting('showUnchargedItems')
- this.showUnequippedItems = Utils.getSetting('showUnequippedItems')
- this.showUnpreparedSpells = Utils.getSetting('showUnpreparedSpells')
-
- // Set subcategory variables
- this.subcategoryIds = subcategoryIds
-
- this.activationSubcategoryIds = subcategoryIds.filter((subcategoryId) =>
- subcategoryId === 'actions' ||
- subcategoryId === 'bonus-actions' ||
- subcategoryId === 'crew-actions' ||
- subcategoryId === 'lair-actions' ||
- subcategoryId === 'legendary-actions' ||
- subcategoryId === 'reactions' ||
- subcategoryId === 'other-actions'
- )
-
- this.effectSubcategoryIds = subcategoryIds.filter((subcategoryId) =>
- subcategoryId === 'passive-effects' ||
- subcategoryId === 'temporary-effects'
- )
-
- this.featureSubcategoryIds = subcategoryIds.filter((subcategoryId) =>
- subcategoryId === 'active-features' ||
- subcategoryId === 'passive-features' ||
- subcategoryId === 'background-features' ||
- subcategoryId === 'class-features' ||
- subcategoryId === 'feats' ||
- subcategoryId === 'monster-features' ||
- subcategoryId === 'race-features' ||
- subcategoryId === 'artificer-infusions' ||
- subcategoryId === 'channel-divinity' ||
- subcategoryId === 'defensive-tactics' ||
- subcategoryId === 'eldritch-invocations' ||
- subcategoryId === 'elemental-disciplines' ||
- subcategoryId === 'fighting-styles' ||
- subcategoryId === 'hunters-prey' ||
- subcategoryId === 'ki-abilities' ||
- subcategoryId === 'maneuvers' ||
- subcategoryId === 'metamagic-options' ||
- subcategoryId === 'multiattacks' ||
- subcategoryId === 'pact-boons' ||
- subcategoryId === 'psionic-powers' ||
- subcategoryId === 'runes' ||
- subcategoryId === 'superior-hunters-defense'
- )
-
- this.spellSubcategoryIds = subcategoryIds.filter((subcategoryId) =>
- subcategoryId === 'cantrips' ||
- subcategoryId === '1st-level-spells' ||
- subcategoryId === '2nd-level-spells' ||
- subcategoryId === '3rd-level-spells' ||
- subcategoryId === '4th-level-spells' ||
- subcategoryId === '5th-level-spells' ||
- subcategoryId === '6th-level-spells' ||
- subcategoryId === '7th-level-spells' ||
- subcategoryId === '8th-level-spells' ||
- subcategoryId === '9th-level-spells' ||
- subcategoryId === 'at-will-spells' ||
- subcategoryId === 'innate-spells' ||
- subcategoryId === 'pact-spells'
- )
-
- // Add subcategory ids for activation types
- if (this.activationSubcategoryIds.length > 0) {
this.featureSubcategoryIds = [
'active-features',
- 'passive-features'
+ 'passive-features',
+ 'background-features',
+ 'class-features',
+ 'feats',
+ 'monster-features',
+ 'race-features',
+ 'artificer-infusions',
+ 'channel-divinity',
+ 'defensive-tactics',
+ 'eldritch-invocations',
+ 'elemental-disciplines',
+ 'fighting-styles',
+ 'hunters-prey',
+ 'ki-abilities',
+ 'maneuvers',
+ 'metamagic-options',
+ 'multiattacks',
+ 'pact-boons',
+ 'psionic-powers',
+ 'runes',
+ 'superior-hunters-defense'
]
+
this.spellSubcategoryIds = [
'cantrips',
'1st-level-spells',
@@ -152,1183 +111,1156 @@ export class ActionHandler extends CoreActionHandler {
'innate-spells',
'pact-spells'
]
- }
- if (this.actorType === 'character' || this.actorType === 'npc') {
- this.inventorySubcategoryIds = subcategoryIds.filter((subcategoryId) =>
- subcategoryId === 'equipped' ||
- subcategoryId === 'consumables' ||
- subcategoryId === 'containers' ||
- subcategoryId === 'equipment' ||
- subcategoryId === 'loot' ||
- subcategoryId === 'tools' ||
- subcategoryId === 'weapons' ||
- subcategoryId === 'unequipped'
- )
-
- // Add subcategory ids for activation types
- if (this.activationSubcategoryIds.length > 0) {
+ if (this.actorType === 'character' || this.actorType === 'npc') {
this.inventorySubcategoryIds = [
+ 'equipped',
'consumables',
'containers',
'equipment',
'loot',
'tools',
- 'weapons'
+ 'weapons',
+ 'unequipped'
]
- }
- this._buildCharacterActions()
- }
- if (this.actorType === 'vehicle') {
- this.inventorySubcategoryIds = this.subcategoryIds.filter((subcategoryId) =>
- subcategoryId === 'consumables' ||
- subcategoryId === 'equipment' ||
- subcategoryId === 'tools' ||
- subcategoryId === 'weapons'
- )
-
- // Add subcategory ids for activation types
- if (this.activationSubcategoryIds.length > 0) {
+ this._buildCharacterActions()
+ }
+ if (this.actorType === 'vehicle') {
this.inventorySubcategoryIds = [
'consumables',
'equipment',
'tools',
'weapons'
]
- }
- this._buildVehicleActions()
- }
- if (!this.actor) {
- this._buildMultipleTokenActions()
+ this._buildVehicleActions()
+ }
+ if (!this.actor) {
+ this._buildMultipleTokenActions()
+ }
}
- }
-
- /**
- * Build Character Actionss
- * @private
- * @returns {object}
- */
- async _buildCharacterActions () {
- this._buildAbilities('ability', 'abilities')
- this._buildAbilities('abilityCheck', 'checks')
- this._buildAbilities('abilitySave', 'saves')
- this._buildCombat()
- this._buildConditions()
- this._buildEffects()
- this._buildFeatures()
- this._buildInventory()
- this._buildRests()
- this._buildSkills()
- this._buildSpells()
- this._buildUtility()
- }
- /**
- * Build Vehicle Actions
- * @private
- * @returns {object}
- */
- async _buildVehicleActions () {
- this._buildAbilities('ability', 'abilities')
- this._buildAbilities('abilityCheck', 'checks')
- this._buildAbilities('abilitySave', 'saves')
- this._buildCombat()
- this._buildConditions()
- this._buildEffects()
- this._buildFeatures()
- this._buildInventory()
- this._buildUtility()
- }
+ /**
+ * Build Character Actions
+ * @private
+ * @returns {object}
+ */
+ async _buildCharacterActions () {
+ this._buildAbilities('ability', 'abilities')
+ this._buildAbilities('check', 'checks')
+ this._buildAbilities('save', 'saves')
+ this._buildCombat()
+ this._buildConditions()
+ this._buildEffects()
+ this._buildFeatures()
+ this._buildInventory()
+ this._buildRests()
+ this._buildSkills()
+ this._buildSpells()
+ this._buildUtility()
+ }
- /**
- * Build Multiple Token Actions
- * @private
- * @returns {object}
- */
- async _buildMultipleTokenActions () {
- this._buildAbilities('ability', 'abilities')
- this._buildAbilities('abilityCheck', 'checks')
- this._buildAbilities('abilitySave', 'saves')
- this._buildCombat()
- this._buildConditions()
- this._buildRests()
- this._buildSkills()
- this._buildUtility()
- }
+ /**
+ * Build Vehicle Actions
+ * @private
+ * @returns {object}
+ */
+ async _buildVehicleActions () {
+ this._buildAbilities('ability', 'abilities')
+ this._buildAbilities('check', 'checks')
+ this._buildAbilities('save', 'saves')
+ this._buildCombat()
+ this._buildConditions()
+ this._buildEffects()
+ this._buildFeatures()
+ this._buildInventory()
+ this._buildUtility()
+ }
- /**
- * Build Abilities
- * @private
- * @param {string} actionType
- * @param {string} subcategoryId
- */
- _buildAbilities (actionType, subcategoryId) {
- // Exit if no subcategory exists
- if (!this.subcategoryIds.includes(subcategoryId)) return
+ /**
+ * Build Multiple Token Actions
+ * @private
+ * @returns {object}
+ */
+ async _buildMultipleTokenActions () {
+ this._buildAbilities('ability', 'abilities')
+ this._buildAbilities('check', 'checks')
+ this._buildAbilities('save', 'saves')
+ this._buildCombat()
+ this._buildConditions()
+ this._buildRests()
+ this._buildSkills()
+ this._buildUtility()
+ }
+ /**
+ * Build Abilities
+ * @private
+ * @param {string} actionType
+ * @param {string} subcategoryId
+ */
+ _buildAbilities (actionType, subcategoryId) {
// Get abilities
- const abilities = (this.actorId === 'multi') ? game.dnd5e.config.abilities : this.actor.system.abilities
-
- // Exit if no abilities exist
- if (abilities.length === 0) return
-
- // Get actions
- const actions = Object.entries(abilities)
- .filter((ability) => abilities[ability[0]].value !== 0)
- .map((ability) => {
- const id = ability[0]
- const abbreviatedName = id.charAt(0).toUpperCase() + id.slice(1)
- const name = this.abbreviateSkills ? abbreviatedName : game.dnd5e.config.abilities[id]
- const encodedValue = [actionType, this.actorId, this.tokenId, id].join(this.delimiter)
- const icon = (subcategoryId !== 'checks') ? this._getProficiencyIcon(abilities[id].proficient) : ''
- return {
- id,
- name,
- encodedValue,
- icon,
- selected: true
- }
- })
+ const abilities = (!this.actor) ? game.dnd5e.config.abilities : this.actor.system.abilities
+
+ // Exit if no abilities exist
+ if (abilities.length === 0) return
+
+ // Get actions
+ const actions = Object.entries(abilities)
+ .filter((ability) => abilities[ability[0]].value !== 0)
+ .map((ability) => {
+ const abilityId = ability[0]
+ const id = `${actionType}-${ability[0]}`
+ const abbreviatedName = abilityId.charAt(0).toUpperCase() + abilityId.slice(1)
+ const name = this.abbreviateSkills ? abbreviatedName : game.dnd5e.config.abilities[abilityId]
+ // Localise
+ const actionTypeName = `${coreModule.api.Utils.i18n(ACTION_TYPE[actionType])}: ` ?? ''
+ const listName = `${actionTypeName}${game.dnd5e.config.abilities[abilityId]}`
+ const encodedValue = [actionType, abilityId].join(this.delimiter)
+ const icon = (subcategoryId !== 'checks') ? this._getProficiencyIcon(abilities[abilityId].proficient) : ''
+ return {
+ id,
+ name,
+ encodedValue,
+ icon,
+ listName
+ }
+ })
- // Create subcategory data
- const subcategoryData = { id: subcategoryId, type: 'system' }
+ // Create subcategory data
+ const subcategoryData = { id: subcategoryId, type: 'system' }
- // Add actions to action list
- this.addActionsToActionList(actions, subcategoryData)
- }
+ // Add actions to action list
+ this.addActionsToActionList(actions, subcategoryData)
+ }
- async buildActivations (items, subcategoryData, actionType = 'item') {
+ async buildActivations (items, subcategoryData, actionType = 'item') {
// Create map of items according to activation type
- const activationItems = new Map()
-
- // Create subcategory mappings
- const subcategoryMappings = {
- actions: 'action',
- 'bonus-actions': 'bonus',
- 'crew-actions': 'crew',
- 'lair-actions': 'lair',
- 'legendary-actions': 'legendary',
- reactions: 'reaction',
- 'other-actions': 'other'
- }
+ const activationItems = new Map()
+
+ // Create subcategory mappings
+ const subcategoryMappings = {
+ actions: 'action',
+ 'bonus-actions': 'bonus',
+ 'crew-actions': 'crew',
+ 'lair-actions': 'lair',
+ 'legendary-actions': 'legendary',
+ reactions: 'reaction',
+ 'other-actions': 'other'
+ }
- const activationTypes = ['action', 'bonus', 'crew', 'lair', 'legendary', 'reaction']
+ const activationTypes = ['action', 'bonus', 'crew', 'lair', 'legendary', 'reaction']
- // Loop through items
- for (const [key, value] of items) {
- const activationType = value.system?.activation?.type
- const activationTypeOther = (activationTypes.includes(activationType)) ? activationType : 'other'
- if (!activationItems.has(activationTypeOther)) activationItems.set(activationTypeOther, new Map())
- activationItems.get(activationTypeOther).set(key, value)
- }
+ // Loop through items
+ for (const [key, value] of items) {
+ const activationType = value.system?.activation?.type
+ const activationTypeOther = (activationTypes.includes(activationType)) ? activationType : 'other'
+ if (!activationItems.has(activationTypeOther)) activationItems.set(activationTypeOther, new Map())
+ activationItems.get(activationTypeOther).set(key, value)
+ }
- // Loop through action subcategory ids
- for (const subcategoryId of this.activationSubcategoryIds) {
- const activationType = subcategoryMappings[subcategoryId]
+ // Loop through action subcategory ids
+ for (const subcategoryId of this.activationSubcategoryIds) {
+ const activationType = subcategoryMappings[subcategoryId]
- // Skip if no items exist
- if (!activationItems.has(activationType)) continue
+ // Skip if no items exist
+ if (!activationItems.has(activationType)) continue
- // Clone and add to subcategory data
- const subcategoryDataClone = { ...subcategoryData, id: `${subcategoryId}+${subcategoryData.id}`, type: 'system-derived' }
+ // Clone and add to subcategory data
+ const subcategoryDataClone = { ...subcategoryData, id: `${subcategoryId}+${subcategoryData.id}`, type: 'system-derived' }
- // Create parent subcategory data
- const parentSubcategoryData = { id: subcategoryId, type: 'system' }
+ // Create parent subcategory data
+ const parentSubcategoryData = { id: subcategoryId, type: 'system' }
- // Add subcategory to action list
- await this.addSubcategoryToActionList(parentSubcategoryData, subcategoryDataClone)
+ // Add subcategory to action list
+ await this.addSubcategoryToActionList(parentSubcategoryData, subcategoryDataClone)
- // Add spell slot info to subcategory
- this.addSubcategoryInfo(subcategoryData)
+ // Add spell slot info to subcategory
+ this.addSubcategoryInfo(subcategoryData)
- // Build actions
- this._buildActions(activationItems.get(activationType), subcategoryDataClone, actionType)
+ // Build actions
+ this._buildActions(activationItems.get(activationType), subcategoryDataClone, actionType)
+ }
}
- }
- /**
- * Build Combat
- * @private
- */
- _buildCombat () {
- // Exit if no subcategory exists
- if (!this.subcategoryIds.includes('combat')) return
-
- const actionType = 'utility'
-
- // Set combat types
- const combatTypes = {
- initiative: { id: 'initiative', name: CoreUtils.i18n('tokenActionHud.dnd5e.rollInitiative') },
- endTurn: { id: 'endTurn', name: CoreUtils.i18n('tokenActionHud.endTurn') }
- }
+ /**
+ * Build Combat
+ * @private
+ */
+ _buildCombat () {
+ const actionType = 'utility'
+
+ // Set combat types
+ const combatTypes = {
+ initiative: { id: 'initiative', name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.rollInitiative') },
+ endTurn: { id: 'endTurn', name: coreModule.api.Utils.i18n('tokenActionHud.endTurn') }
+ }
- // Delete endTurn for multiple tokens
- if (game.combat?.current?.tokenId !== this.tokenId) delete combatTypes.endTurn
+ // Delete endTurn for multiple tokens
+ if (game.combat?.current?.tokenId !== this.token?.id) delete combatTypes.endTurn
+
+ // Get actions
+ const actions = Object.entries(combatTypes).map((combatType) => {
+ const id = combatType[1].id
+ const name = combatType[1].name
+ const actionTypeName = `${coreModule.api.Utils.i18n(ACTION_TYPE[actionType])}: ` ?? ''
+ const listName = `${actionTypeName}${name}`
+ const encodedValue = [actionType, id].join(this.delimiter)
+ const info1 = {}
+ let cssClass = ''
+ if (combatType[0] === 'initiative' && game.combat) {
+ const tokenIds = canvas.tokens.controlled.map((token) => token.id)
+ const combatants = game.combat.combatants.filter((combatant) => tokenIds.includes(combatant.tokenId))
+
+ // Get initiative for single token
+ if (combatants.length === 1) {
+ const currentInitiative = combatants[0].initiative
+ info1.class = 'tah-spotlight'
+ info1.text = currentInitiative
+ }
- // Get actions
- const actions = Object.entries(combatTypes).map((combatType) => {
- const id = combatType[1].id
- const name = combatType[1].name
- const encodedValue = [actionType, this.actorId, this.tokenId, id].join(this.delimiter)
- const info1 = {}
- let cssClass = ''
- if (combatType[0] === 'initiative' && game.combat) {
- const tokenIds = canvas.tokens.controlled.map((token) => token.id)
- const combatants = game.combat.combatants.filter((combatant) => tokenIds.includes(combatant.tokenId))
-
- // Get initiative for single token
- if (combatants.length === 1) {
- const currentInitiative = combatants[0].initiative
- info1.class = 'tah-spotlight'
- info1.text = currentInitiative
+ const active = combatants.length > 0 && (combatants.every((combatant) => combatant?.initiative)) ? ' active' : ''
+ cssClass = `toggle${active}`
}
+ return {
+ id,
+ name,
+ encodedValue,
+ info1,
+ cssClass,
+ listName
+ }
+ })
- const active = combatants.length > 0 && (combatants.every((combatant) => combatant?.initiative)) ? ' active' : ''
- cssClass = `toggle${active}`
- }
- return {
- id,
- name,
- encodedValue,
- info1,
- cssClass,
- selected: true
- }
- })
-
- // Create subcategory data
- const subcategoryData = { id: 'combat', type: 'system' }
+ // Create subcategory data
+ const subcategoryData = { id: 'combat', type: 'system' }
- // Add actions to action list
- this.addActionsToActionList(actions, subcategoryData)
- }
+ // Add actions to action list
+ this.addActionsToActionList(actions, subcategoryData)
+ }
- /**
- * Build Conditions
- * @private
- */
- _buildConditions () {
- // Exit if the no subcategory or token exists
- if (!this.subcategoryIds.includes('conditions')) return
- if (!this.token) return
-
- const actionType = 'condition'
-
- // Get conditions
- const conditions = CONFIG.statusEffects.filter((condition) => condition.id !== '')
-
- // Exit if no conditions exist
- if (conditions.length === 0) return
-
- // Get actions
- const actions = conditions.map((condition) => {
- const id = condition.id
- const name = CoreUtils.i18n(condition.label)
- const encodedValue = [actionType, this.actorId, this.tokenId, id].join(this.delimiter)
- const active = this.actors.every((actor) => {
- const effects = actor.effects
- return effects
- .map((effect) => effect.flags?.core?.statusId)
- .some((statusId) => statusId === id)
+ /**
+ * Build Conditions
+ * @private
+ */
+ _buildConditions () {
+ if (!this.token && this.tokens.length === 0) return
+
+ const actionType = 'condition'
+
+ // Get conditions
+ const conditions = CONFIG.statusEffects.filter((condition) => condition.id !== '')
+
+ // Exit if no conditions exist
+ if (conditions.length === 0) return
+
+ // Get actions
+ const actions = conditions.map((condition) => {
+ const id = condition.id
+ const name = coreModule.api.Utils.i18n(condition.label)
+ const actionTypeName = `${coreModule.api.Utils.i18n(ACTION_TYPE[actionType])}: ` ?? ''
+ const listName = `${actionTypeName}${name}`
+ const encodedValue = [actionType, id].join(this.delimiter)
+ const active = this.actors.every((actor) => {
+ const effects = actor.effects
+ return effects
+ .map((effect) => effect.flags?.core?.statusId)
+ .some((statusId) => statusId === id)
+ })
+ ? ' active'
+ : ''
+ const cssClass = `toggle${active}`
+ const img = coreModule.api.Utils.getImage(condition)
+ return {
+ id,
+ name,
+ encodedValue,
+ img,
+ cssClass,
+ listName
+ }
})
- ? ' active'
- : ''
- const cssClass = `toggle${active}`
- const img = CoreUtils.getImage(condition)
- return {
- id,
- name,
- encodedValue,
- img,
- cssClass,
- selected: true
- }
- })
-
- // Create subcategory data
- const subcategoryData = { id: 'conditions', type: 'system' }
-
- // Add actions to action list
- this.addActionsToActionList(actions, subcategoryData)
- }
- /**
- * Build Effects
- * @private
- */
- _buildEffects () {
- // Exit if no subcategories exist
- if (!this.effectSubcategoryIds) return
+ // Create subcategory data
+ const subcategoryData = { id: 'conditions', type: 'system' }
- const actionType = 'effect'
+ // Add actions to action list
+ this.addActionsToActionList(actions, subcategoryData)
+ }
- // Get effects
- const effects = this.actor.effects
+ /**
+ * Build Effects
+ * @private
+ */
+ _buildEffects () {
+ const actionType = 'effect'
+
+ // Get effects
+ const effects = this.actor.effects
+
+ // Exit if no effects exist
+ if (effects.size === 0) return
+
+ // Map passive and temporary effects to new maps
+ const passiveEffects = new Map()
+ const temporaryEffects = new Map()
+
+ // Iterate effects and add to a map based on the isTemporary value
+ for (const effect of effects) {
+ const key = effect.id
+ const isTemporary = effect.isTemporary
+ if (isTemporary) {
+ temporaryEffects.set(key, effect)
+ } else {
+ passiveEffects.set(key, effect)
+ }
+ }
- // Exit if no effects exist
- if (effects.size === 0) return
+ // Build passive effects
+ this._buildActions(passiveEffects, { id: 'passive-effects', type: 'system' }, actionType)
- // Map passive and temporary effects to new maps
- const passiveEffects = new Map()
- const temporaryEffects = new Map()
+ // Build temporary effects
+ this._buildActions(temporaryEffects, { id: 'temporary-effects', type: 'system' }, actionType)
+ }
- // Iterate effects and add to a map based on the isTemporary value
- for (const effect of effects) {
- const key = effect.id
- const isTemporary = effect.isTemporary
- if (isTemporary) {
- temporaryEffects.set(key, effect)
- } else {
- passiveEffects.set(key, effect)
+ /**
+ * Build Features
+ * @private
+ */
+ _buildFeatures () {
+ const actionType = 'feature'
+
+ // Get feats
+ const feats = new Map()
+ for (const [key, value] of this.items) {
+ const type = value.type
+ if (type === 'feat') feats.set(key, value)
}
- }
- // Build passive effects
- if (this.effectSubcategoryIds.includes('passive-effects')) {
- const subcategoryData = { id: 'passive-effects', type: 'system' }
- this._buildActions(passiveEffects, subcategoryData, actionType)
- }
+ // Early exit if no feats exist
+ if (feats.size === 0) return
- // Build temporary effects
- if (this.effectSubcategoryIds.includes('temporary-effects')) {
- const subcategoryData = { id: 'temporary-effects', type: 'system' }
- this._buildActions(temporaryEffects, subcategoryData, actionType)
- }
- }
+ // Map active and passive features to new maps
+ const featuresMap = new Map()
- /**
- * Build Features
- * @private
- */
- _buildFeatures () {
- // Exit if no subcategories exist
- if (!this.featureSubcategoryIds) return
-
- const actionType = 'feature'
-
- // Get feats
- const feats = new Map()
- for (const [key, value] of this.items) {
- const type = value.type
- if (type === 'feat') feats.set(key, value)
- }
+ const featureTypes = [
+ { type: 'background', subcategoryId: 'background-features' },
+ { type: 'class', subcategoryId: 'class-features' },
+ { type: 'monster', subcategoryId: 'monster-features' },
+ { type: 'race', subcategoryId: 'race-features' },
+ { type: 'feats', subcategoryId: 'feats' }
+ ]
- // Early exit if no feats exist
- if (feats.size === 0) return
-
- // Map active and passive features to new maps
- const featuresMap = new Map()
-
- const featureTypes = [
- { type: 'background', subcategoryId: 'background-features' },
- { type: 'class', subcategoryId: 'class-features' },
- { type: 'monster', subcategoryId: 'monster-features' },
- { type: 'race', subcategoryId: 'race-features' },
- { type: 'feats', subcategoryId: 'feats' }
- ]
-
- const classFeatureTypes = [
- { type: 'artificerInfusion', subcategoryId: 'artificer-infusions' },
- { type: 'channelDivinity', subcategoryId: 'channel-divinity' },
- { type: 'defensiveTactic', subcategoryId: 'defensive-tactics' },
- { type: 'eldritchInvocation', subcategoryId: 'eldritch-invocations' },
- { type: 'elementalDiscipline', subcategoryId: 'elemental-disciplines' },
- { type: 'fightingStyle', subcategoryId: 'fighting-styles' },
- { type: 'huntersPrey', subcategoryId: 'hunters-prey' },
- { type: 'ki', subcategoryId: 'ki-abilities' },
- { type: 'maneuver', subcategoryId: 'maneuvers' },
- { type: 'metamagic', subcategoryId: 'metamagic-options' },
- { type: 'multiattack', subcategoryId: 'multiattacks' },
- { type: 'pact', subcategoryId: 'pact-boons' },
- { type: 'psionicPower', subcategoryId: 'psionic-powers' },
- { type: 'rune', subcategoryId: 'runes' },
- { type: 'superiorHuntersDefense', subcategoryId: 'superior-hunters-defense' }
- ]
-
- for (const [key, value] of feats) {
- const activationType = value.system.activation?.type
- const type = value.system.type.value
- const subType = value.system.type?.subtype
- const excludedActivationTypes = ['', 'lair', 'legendary']
- if (activationType && !excludedActivationTypes.includes(activationType)) {
- if (!featuresMap.has('active-features')) featuresMap.set('active-features', new Map())
- featuresMap.get('active-features').set(key, value)
- }
- if (!activationType || activationType === '') {
- if (!featuresMap.has('passive-features')) featuresMap.set('passive-features', new Map())
- featuresMap.get('passive-features').set(key, value)
- }
- for (const featureType of featureTypes) {
- const subcategoryId = featureType.subcategoryId
- if (featureType.type === type) {
- if (!featuresMap.has(subcategoryId)) featuresMap.set(subcategoryId, new Map())
- featuresMap.get(subcategoryId).set(key, value)
+ const classFeatureTypes = [
+ { type: 'artificerInfusion', subcategoryId: 'artificer-infusions' },
+ { type: 'channelDivinity', subcategoryId: 'channel-divinity' },
+ { type: 'defensiveTactic', subcategoryId: 'defensive-tactics' },
+ { type: 'eldritchInvocation', subcategoryId: 'eldritch-invocations' },
+ { type: 'elementalDiscipline', subcategoryId: 'elemental-disciplines' },
+ { type: 'fightingStyle', subcategoryId: 'fighting-styles' },
+ { type: 'huntersPrey', subcategoryId: 'hunters-prey' },
+ { type: 'ki', subcategoryId: 'ki-abilities' },
+ { type: 'maneuver', subcategoryId: 'maneuvers' },
+ { type: 'metamagic', subcategoryId: 'metamagic-options' },
+ { type: 'multiattack', subcategoryId: 'multiattacks' },
+ { type: 'pact', subcategoryId: 'pact-boons' },
+ { type: 'psionicPower', subcategoryId: 'psionic-powers' },
+ { type: 'rune', subcategoryId: 'runes' },
+ { type: 'superiorHuntersDefense', subcategoryId: 'superior-hunters-defense' }
+ ]
+
+ for (const [key, value] of feats) {
+ const activationType = value.system.activation?.type
+ const type = value.system.type.value
+ const subType = value.system.type?.subtype
+ const excludedActivationTypes = ['', 'lair', 'legendary']
+ if (activationType && !excludedActivationTypes.includes(activationType)) {
+ if (!featuresMap.has('active-features')) featuresMap.set('active-features', new Map())
+ featuresMap.get('active-features').set(key, value)
}
- }
- for (const featureType of classFeatureTypes) {
- const subcategoryId = featureType.subcategoryId
- if (subType && featureType.type === subType) {
- if (!featuresMap.has(subcategoryId)) featuresMap.set(subcategoryId, new Map())
- featuresMap.get(subcategoryId).set(key, value)
+ if (!activationType || activationType === '') {
+ if (!featuresMap.has('passive-features')) featuresMap.set('passive-features', new Map())
+ featuresMap.get('passive-features').set(key, value)
+ }
+ for (const featureType of featureTypes) {
+ const subcategoryId = featureType.subcategoryId
+ if (featureType.type === type) {
+ if (!featuresMap.has(subcategoryId)) featuresMap.set(subcategoryId, new Map())
+ featuresMap.get(subcategoryId).set(key, value)
+ }
+ }
+ for (const featureType of classFeatureTypes) {
+ const subcategoryId = featureType.subcategoryId
+ if (subType && featureType.type === subType) {
+ if (!featuresMap.has(subcategoryId)) featuresMap.set(subcategoryId, new Map())
+ featuresMap.get(subcategoryId).set(key, value)
+ }
}
}
- }
- // Create subcategory name mappings
- const subcategoryNameMappings = {
- 'active-features': CoreUtils.i18n('tokenActionHud.dnd5e.activeFeatures'),
- 'passive-features': CoreUtils.i18n('tokenActionHud.dnd5e.passiveFeatures')
- }
+ // Create subcategory name mappings
+ const subcategoryNameMappings = {
+ 'active-features': coreModule.api.Utils.i18n('tokenActionHud.dnd5e.activeFeatures'),
+ 'passive-features': coreModule.api.Utils.i18n('tokenActionHud.dnd5e.passiveFeatures')
+ }
- // Loop through inventory subcateogry ids
- for (const subcategoryId of this.featureSubcategoryIds) {
- if (!featuresMap.has(subcategoryId)) continue
+ // Loop through inventory subcateogry ids
+ for (const subcategoryId of this.featureSubcategoryIds) {
+ if (!featuresMap.has(subcategoryId)) continue
- // Create subcategory data
- const subcategoryData = {
- id: subcategoryId,
- name: subcategoryNameMappings[subcategoryId] ?? '',
- type: 'system'
- }
+ // Create subcategory data
+ const subcategoryData = {
+ id: subcategoryId,
+ name: subcategoryNameMappings[subcategoryId] ?? '',
+ type: 'system'
+ }
- const features = featuresMap.get(subcategoryId)
+ const features = featuresMap.get(subcategoryId)
- // Build actions
- this._buildActions(features, subcategoryData, actionType)
+ // Build actions
+ this._buildActions(features, subcategoryData, actionType)
- // Build activations
- if (subcategoryNameMappings[subcategoryId]) this.buildActivations(features, subcategoryData, actionType)
+ // Build activations
+ if (subcategoryNameMappings[subcategoryId]) this.buildActivations(features, subcategoryData, actionType)
+ }
}
- }
- /**
- * Build Inventory
- * @private
- */
- _buildInventory () {
- // Exit if no subcategories exist
- if (!this.inventorySubcategoryIds) return
+ /**
+ * Build Inventory
+ * @private
+ */
+ _buildInventory () {
// Exit early if no items exist
- if (this.items.size === 0) return
+ if (this.items.size === 0) return
- const inventoryMap = new Map()
+ const inventoryMap = new Map()
- for (const [key, value] of this.items) {
+ for (const [key, value] of this.items) {
// Set variables
- const equipped = value.system.equipped
- const hasQuantity = value.system?.quantity > 0
- const isActiveItem = this._isActiveItem(value)
- const isUsableItem = this._isUsableItem(value)
- const isEquippedItem = this._isEquippedItem(value)
- const type = value.type
-
- // Set items into maps
- if (hasQuantity && isActiveItem) {
- if (equipped) {
- if (!inventoryMap.has('equipped')) inventoryMap.set('equipped', new Map())
- inventoryMap.get('equipped').set(key, value)
- }
- if (!equipped) {
- if (!inventoryMap.has('unequipped')) inventoryMap.set('unequipped', new Map())
- inventoryMap.get('unequipped').set(key, value)
- }
- if (isUsableItem && type === 'consumable') {
- if (!inventoryMap.has('consumables')) inventoryMap.set('consumables', new Map())
- inventoryMap.get('consumables').set(key, value)
- }
- if (isEquippedItem) {
- if (type === 'backpack') {
- if (!inventoryMap.has('containers')) inventoryMap.set('containers', new Map())
- inventoryMap.get('containers').set(key, value)
- }
- if (type === 'equipment') {
- if (!inventoryMap.has('equipment')) inventoryMap.set('equipment', new Map())
- inventoryMap.get('equipment').set(key, value)
+ const equipped = value.system.equipped
+ const hasQuantity = value.system?.quantity > 0
+ const isActiveItem = this._isActiveItem(value)
+ const isUsableItem = this._isUsableItem(value)
+ const isEquippedItem = this._isEquippedItem(value)
+ const type = value.type
+
+ // Set items into maps
+ if (hasQuantity && isActiveItem) {
+ if (equipped) {
+ if (!inventoryMap.has('equipped')) inventoryMap.set('equipped', new Map())
+ inventoryMap.get('equipped').set(key, value)
}
- if (type === 'loot') {
- if (!inventoryMap.has('loot')) inventoryMap.set('loot', new Map())
- inventoryMap.get('loot').set(key, value)
+ if (!equipped) {
+ if (!inventoryMap.has('unequipped')) inventoryMap.set('unequipped', new Map())
+ inventoryMap.get('unequipped').set(key, value)
}
- if (type === 'tool') {
- if (!inventoryMap.has('tools')) inventoryMap.set('tools', new Map())
- inventoryMap.get('tools').set(key, value)
+ if (isUsableItem && type === 'consumable') {
+ if (!inventoryMap.has('consumables')) inventoryMap.set('consumables', new Map())
+ inventoryMap.get('consumables').set(key, value)
}
- if (type === 'weapon') {
- if (!inventoryMap.has('weapons')) inventoryMap.set('weapons', new Map())
- inventoryMap.get('weapons').set(key, value)
+ if (isEquippedItem) {
+ if (type === 'backpack') {
+ if (!inventoryMap.has('containers')) inventoryMap.set('containers', new Map())
+ inventoryMap.get('containers').set(key, value)
+ }
+ if (type === 'equipment') {
+ if (!inventoryMap.has('equipment')) inventoryMap.set('equipment', new Map())
+ inventoryMap.get('equipment').set(key, value)
+ }
+ if (type === 'loot') {
+ if (!inventoryMap.has('loot')) inventoryMap.set('loot', new Map())
+ inventoryMap.get('loot').set(key, value)
+ }
+ if (type === 'tool') {
+ if (!inventoryMap.has('tools')) inventoryMap.set('tools', new Map())
+ inventoryMap.get('tools').set(key, value)
+ }
+ if (type === 'weapon') {
+ if (!inventoryMap.has('weapons')) inventoryMap.set('weapons', new Map())
+ inventoryMap.get('weapons').set(key, value)
+ }
}
}
}
- }
-
- // Create subcategory name mappings
- const subcategoryNameMappings = {
- equipped: CoreUtils.i18n('DND5E.Equipped'),
- unequipped: CoreUtils.i18n('DND5E.Unequipped'),
- consumables: CoreUtils.i18n('ITEM.TypeConsumablePl'),
- containers: CoreUtils.i18n('ITEM.TypeContainerPl'),
- equipment: CoreUtils.i18n('ITEM.TypeEquipmentPl'),
- loot: CoreUtils.i18n('ITEM.TypeLootPl'),
- tools: CoreUtils.i18n('ITEM.TypeToolPl'),
- weapons: CoreUtils.i18n('ITEM.TypeWeaponPl')
- }
-
- // Loop through inventory subcateogry ids
- for (const subcategoryId of this.inventorySubcategoryIds) {
- if (!inventoryMap.has(subcategoryId)) continue
- // Create subcategory data
- const subcategoryData = {
- id: subcategoryId,
- name: subcategoryNameMappings[subcategoryId],
- type: 'system'
+ // Create subcategory name mappings
+ const subcategoryNameMappings = {
+ equipped: coreModule.api.Utils.i18n('DND5E.Equipped'),
+ unequipped: coreModule.api.Utils.i18n('DND5E.Unequipped'),
+ consumables: coreModule.api.Utils.i18n('ITEM.TypeConsumablePl'),
+ containers: coreModule.api.Utils.i18n('ITEM.TypeContainerPl'),
+ equipment: coreModule.api.Utils.i18n('ITEM.TypeEquipmentPl'),
+ loot: coreModule.api.Utils.i18n('ITEM.TypeLootPl'),
+ tools: coreModule.api.Utils.i18n('ITEM.TypeToolPl'),
+ weapons: coreModule.api.Utils.i18n('ITEM.TypeWeaponPl')
}
- const inventory = inventoryMap.get(subcategoryId)
-
- // Build actions
- this._buildActions(inventory, subcategoryData)
+ // Loop through inventory subcateogry ids
+ for (const subcategoryId of this.inventorySubcategoryIds) {
+ if (!inventoryMap.has(subcategoryId)) continue
- // Build activations
- if (this.activationSubcategoryIds) this.buildActivations(inventory, subcategoryData)
- }
- }
-
- /**
- * Build Rests
- * @private
- */
- _buildRests () {
- // Exit if no subcategory exists
- if (!this.subcategoryIds.includes('rests')) return
+ // Create subcategory data
+ const subcategoryData = {
+ id: subcategoryId,
+ name: subcategoryNameMappings[subcategoryId],
+ type: 'system'
+ }
- // Exit if every actor is not the character type
- if (!this.actors.every(actor => actor.type === 'character')) return
+ const inventory = inventoryMap.get(subcategoryId)
- const actionType = 'utility'
+ // Build actions
+ this._buildActions(inventory, subcategoryData)
- // Set rest types
- const restTypes = {
- shortRest: { name: CoreUtils.i18n('DND5E.ShortRest') },
- longRest: { name: CoreUtils.i18n('DND5E.LongRest') }
+ // Build activations
+ if (this.activationSubcategoryIds) this.buildActivations(inventory, subcategoryData)
+ }
}
- // Get actions
- const actions = Object.entries(restTypes)
- .map((restType) => {
- const id = restType[0]
- const name = restType[1].name
- const encodedValue = [actionType, this.actorId, this.tokenId, id].join(this.delimiter)
- return {
- id,
- name,
- encodedValue,
- selected: true
- }
- })
+ /**
+ * Build Rests
+ * @private
+ */
+ _buildRests () {
+ // Exit if every actor is not the character type
+ if (!this.actors.every(actor => actor.type === 'character')) return
- // Create subcategory data
- const subcategoryData = { id: 'rests', type: 'system' }
+ const actionType = 'utility'
- // Add actions to action list
- this.addActionsToActionList(actions, subcategoryData)
- }
+ // Set rest types
+ const restTypes = {
+ shortRest: { name: coreModule.api.Utils.i18n('DND5E.ShortRest') },
+ longRest: { name: coreModule.api.Utils.i18n('DND5E.LongRest') }
+ }
- /**
- * Build Skills
- * @private
- */
- _buildSkills () {
- // Exit if no subcategory exists
- if (!this.subcategoryIds.includes('skills')) return
-
- const actionType = 'skill'
-
- // Get skills
- const skills = (this.actorId === 'multi') ? game.dnd5e.config.skills : this.actor.system.skills
-
- // Exit if there are no skills
- if (skills.length === 0) return
-
- // Get actions
- const actions = Object.entries(skills)
- .map((skill) => {
- try {
- const id = skill[0]
- const abbreviatedName = id.charAt(0).toUpperCase() + id.slice(1)
- const name = this.abbreviateSkills ? abbreviatedName : game.dnd5e.config.skills[id].label
- const encodedValue = [actionType, this.actorId, this.tokenId, id].join(this.delimiter)
- const icon = this._getProficiencyIcon(skills[id].value)
+ // Get actions
+ const actions = Object.entries(restTypes)
+ .map((restType) => {
+ const id = restType[0]
+ const name = restType[1].name
+ const actionTypeName = `${coreModule.api.Utils.i18n(ACTION_TYPE[actionType])}: ` ?? ''
+ const listName = `${actionTypeName}${name}`
+ const encodedValue = [actionType, id].join(this.delimiter)
return {
id,
name,
encodedValue,
- icon
+ listName
}
- } catch (error) {
- Logger.error(skill)
- return null
- }
- })
- .filter((skill) => !!skill)
+ })
- // Create subcategory data
- const subcategoryData = { id: 'skills', type: 'system' }
+ // Create subcategory data
+ const subcategoryData = { id: 'rests', type: 'system' }
- // Add actions to action list
- this.addActionsToActionList(actions, subcategoryData)
- }
+ // Add actions to action list
+ this.addActionsToActionList(actions, subcategoryData)
+ }
- /**
- * Build Spells
- */
- _buildSpells () {
- // Exit if no subcategories exist
- if (!this.spellSubcategoryIds) return
+ /**
+ * Build Skills
+ * @private
+ */
+ _buildSkills () {
+ const actionType = 'skill'
+
+ // Get skills
+ const skills = (!this.actor) ? game.dnd5e.config.skills : this.actor.system.skills
+
+ // Exit if there are no skills
+ if (skills.length === 0) return
+
+ // Get actions
+ const actions = Object.entries(skills)
+ .map((skill) => {
+ try {
+ const id = skill[0]
+ const abbreviatedName = id.charAt(0).toUpperCase() + id.slice(1)
+ const name = this.abbreviateSkills ? abbreviatedName : game.dnd5e.config.skills[id].label
+ const actionTypeName = `${coreModule.api.Utils.i18n(ACTION_TYPE[actionType])}: ` ?? ''
+ const listName = `${actionTypeName}${game.dnd5e.config.skills[id].label}`
+ const encodedValue = [actionType, id].join(this.delimiter)
+ const icon = this._getProficiencyIcon(skills[id].value)
+ return {
+ id,
+ name,
+ encodedValue,
+ icon,
+ listName
+ }
+ } catch (error) {
+ coreModule.api.Logger.error(skill)
+ return null
+ }
+ })
+ .filter((skill) => !!skill)
- const actionType = 'spell'
+ // Create subcategory data
+ const subcategoryData = { id: 'skills', type: 'system' }
- const spellsMap = new Map()
+ // Add actions to action list
+ this.addActionsToActionList(actions, subcategoryData)
+ }
- // Loop through items
- for (const [key, value] of this.items) {
- const type = value.type
- if (type === 'spell') {
- const isUsableItem = this._isUsableItem(value)
- const isUsableSpell = this._isUsableSpell(value)
- if (isUsableItem && isUsableSpell) {
- const preparationMode = value.system.preparation.mode
- switch (preparationMode) {
- case 'atwill':
- if (!spellsMap.has('at-will-spells')) spellsMap.set('at-will-spells', new Map())
- spellsMap.get('at-will-spells').set(key, value)
- break
- case 'innate':
- if (!spellsMap.has('innate-spells')) spellsMap.set('innate-spells', new Map())
- spellsMap.get('innate-spells').set(key, value)
- break
- case 'pact':
- if (!spellsMap.has('pact-spells')) spellsMap.set('pact-spells', new Map())
- spellsMap.get('pact-spells').set(key, value)
- break
- default:
- { const level = value.system.level
- switch (level) {
- case 0:
- if (!spellsMap.has('cantrips')) spellsMap.set('cantrips', new Map())
- spellsMap.get('cantrips').set(key, value)
+ /**
+ * Build Spells
+ */
+ _buildSpells () {
+ const actionType = 'spell'
+
+ const spellsMap = new Map()
+
+ // Loop through items
+ for (const [key, value] of this.items) {
+ const type = value.type
+ if (type === 'spell') {
+ const isUsableItem = this._isUsableItem(value)
+ const isUsableSpell = this._isUsableSpell(value)
+ if (isUsableItem && isUsableSpell) {
+ const preparationMode = value.system.preparation.mode
+ switch (preparationMode) {
+ case 'atwill':
+ if (!spellsMap.has('at-will-spells')) spellsMap.set('at-will-spells', new Map())
+ spellsMap.get('at-will-spells').set(key, value)
break
- case 1:
- if (!spellsMap.has('1st-level-spells')) spellsMap.set('1st-level-spells', new Map())
- spellsMap.get('1st-level-spells').set(key, value)
+ case 'innate':
+ if (!spellsMap.has('innate-spells')) spellsMap.set('innate-spells', new Map())
+ spellsMap.get('innate-spells').set(key, value)
break
- case 2:
- if (!spellsMap.has('2nd-level-spells')) spellsMap.set('2nd-level-spells', new Map())
- spellsMap.get('2nd-level-spells').set(key, value)
- break
- case 3:
- if (!spellsMap.has('3rd-level-spells')) spellsMap.set('3rd-level-spells', new Map())
- spellsMap.get('3rd-level-spells').set(key, value)
- break
- case 4:
- if (!spellsMap.has('4th-level-spells')) spellsMap.set('4th-level-spells', new Map())
- spellsMap.get('4th-level-spells').set(key, value)
- break
- case 5:
- if (!spellsMap.has('5th-level-spells')) spellsMap.set('5th-level-spells', new Map())
- spellsMap.get('5th-level-spells').set(key, value)
- break
- case 6:
- if (!spellsMap.has('6th-level-spells')) spellsMap.set('6th-level-spells', new Map())
- spellsMap.get('6th-level-spells').set(key, value)
- break
- case 7:
- if (!spellsMap.has('7th-level-spells')) spellsMap.set('7th-level-spells', new Map())
- spellsMap.get('7th-level-spells').set(key, value)
- break
- case 8:
- if (!spellsMap.has('8th-level-spells')) spellsMap.set('8th-level-spells', new Map())
- spellsMap.get('8th-level-spells').set(key, value)
- break
- case 9:
- if (!spellsMap.has('9th-level-spells')) spellsMap.set('9th-level-spells', new Map())
- spellsMap.get('9th-level-spells').set(key, value)
+ case 'pact':
+ if (!spellsMap.has('pact-spells')) spellsMap.set('pact-spells', new Map())
+ spellsMap.get('pact-spells').set(key, value)
break
+ default:
+ { const level = value.system.level
+ switch (level) {
+ case 0:
+ if (!spellsMap.has('cantrips')) spellsMap.set('cantrips', new Map())
+ spellsMap.get('cantrips').set(key, value)
+ break
+ case 1:
+ if (!spellsMap.has('1st-level-spells')) spellsMap.set('1st-level-spells', new Map())
+ spellsMap.get('1st-level-spells').set(key, value)
+ break
+ case 2:
+ if (!spellsMap.has('2nd-level-spells')) spellsMap.set('2nd-level-spells', new Map())
+ spellsMap.get('2nd-level-spells').set(key, value)
+ break
+ case 3:
+ if (!spellsMap.has('3rd-level-spells')) spellsMap.set('3rd-level-spells', new Map())
+ spellsMap.get('3rd-level-spells').set(key, value)
+ break
+ case 4:
+ if (!spellsMap.has('4th-level-spells')) spellsMap.set('4th-level-spells', new Map())
+ spellsMap.get('4th-level-spells').set(key, value)
+ break
+ case 5:
+ if (!spellsMap.has('5th-level-spells')) spellsMap.set('5th-level-spells', new Map())
+ spellsMap.get('5th-level-spells').set(key, value)
+ break
+ case 6:
+ if (!spellsMap.has('6th-level-spells')) spellsMap.set('6th-level-spells', new Map())
+ spellsMap.get('6th-level-spells').set(key, value)
+ break
+ case 7:
+ if (!spellsMap.has('7th-level-spells')) spellsMap.set('7th-level-spells', new Map())
+ spellsMap.get('7th-level-spells').set(key, value)
+ break
+ case 8:
+ if (!spellsMap.has('8th-level-spells')) spellsMap.set('8th-level-spells', new Map())
+ spellsMap.get('8th-level-spells').set(key, value)
+ break
+ case 9:
+ if (!spellsMap.has('9th-level-spells')) spellsMap.set('9th-level-spells', new Map())
+ spellsMap.get('9th-level-spells').set(key, value)
+ break
+ }
+ }
}
- }
}
}
}
- }
- // Reverse sort spell slots by level
- const systemSpells = Object.entries(this.actor.system.spells).reverse()
-
- // Set spell slot availability
- let pactSlot = null
- const spellSlots = []
- let spellSlotAvailable = this.showUnchargedItems
- let pactSlotAvailable = this.showUnchargedItems
- for (const [key, value] of systemSpells) {
- const hasValue = value.value > 0
- const hasMax = value.max > 0
- const hasLevel = value.level > 0
- if (key === 'pact') {
- if (!pactSlotAvailable && hasValue && hasMax && hasLevel) pactSlotAvailable = true
- if (!hasLevel) pactSlotAvailable = false
- value.slotAvailable = pactSlotAvailable
- pactSlot = [key, value]
- }
- if (key.startsWith('spell') && key !== 'spell0') {
- if (!spellSlotAvailable && hasValue && hasMax) spellSlotAvailable = true
- value.slotAvailable = spellSlotAvailable
- spellSlots.push([key, value])
- } else {
- if (hasValue) {
- value.slotsAvailable = true
- spellSlots.push(key, value)
+ // Reverse sort spell slots by level
+ const systemSpells = Object.entries(this.actor.system.spells).reverse()
+
+ // Set spell slot availability
+ let pactSlot = null
+ const spellSlots = []
+ let spellSlotAvailable = this.showUnchargedItems
+ let pactSlotAvailable = this.showUnchargedItems
+ for (const [key, value] of systemSpells) {
+ const hasValue = value.value > 0
+ const hasMax = value.max > 0
+ const hasLevel = value.level > 0
+ if (key === 'pact') {
+ if (!pactSlotAvailable && hasValue && hasMax && hasLevel) pactSlotAvailable = true
+ if (!hasLevel) pactSlotAvailable = false
+ value.slotAvailable = pactSlotAvailable
+ pactSlot = [key, value]
+ }
+ if (key.startsWith('spell') && key !== 'spell0') {
+ if (!spellSlotAvailable && hasValue && hasMax) spellSlotAvailable = true
+ value.slotAvailable = spellSlotAvailable
+ spellSlots.push([key, value])
+ } else {
+ if (hasValue) {
+ value.slotsAvailable = true
+ spellSlots.push(key, value)
+ }
}
}
- }
- // Set equivalent spell slot where pact slot is available
- if (pactSlot[1].slotAvailable) {
- const pactSpellEquivalent = spellSlots.findIndex(spell => spell[0] === 'spell' + pactSlot[1].level)
- spellSlots[pactSpellEquivalent][1].slotsAvailable = true
- }
+ // Set equivalent spell slot where pact slot is available
+ if (pactSlot[1].slotAvailable) {
+ const pactSpellEquivalent = spellSlots.findIndex(spell => spell[0] === 'spell' + pactSlot[1].level)
+ spellSlots[pactSpellEquivalent][1].slotsAvailable = true
+ }
- const subcategoryMappings = {
- '1st-level-spells': { spellMode: 1, name: CoreUtils.i18n('tokenActionHud.dnd5e.1stLevelSpells') },
- '2nd-level-spells': { spellMode: 2, name: CoreUtils.i18n('tokenActionHud.dnd5e.2ndLevelSpells') },
- '3rd-level-spells': { spellMode: 3, name: CoreUtils.i18n('tokenActionHud.dnd5e.3rdLevelSpells') },
- '4th-level-spells': { spellMode: 4, name: CoreUtils.i18n('tokenActionHud.dnd5e.4thLevelSpells') },
- '5th-level-spells': { spellMode: 5, name: CoreUtils.i18n('tokenActionHud.dnd5e.5thLevelSpells') },
- '6th-level-spells': { spellMode: 6, name: CoreUtils.i18n('tokenActionHud.dnd5e.6thLevelSpells') },
- '7th-level-spells': { spellMode: 7, name: CoreUtils.i18n('tokenActionHud.dnd5e.7thLevelSpells') },
- '8th-level-spells': { spellMode: 8, name: CoreUtils.i18n('tokenActionHud.dnd5e.8thLevelSpells') },
- '9th-level-spells': { spellMode: 9, name: CoreUtils.i18n('tokenActionHud.dnd5e.9thLevelSpells') },
- 'at-will-spells': { spellMode: 'atwill', name: CoreUtils.i18n('tokenActionHud.dnd5e.atWillSpells') },
- cantrips: { spellMode: 0, name: CoreUtils.i18n('tokenActionHud.dnd5e.cantrips') },
- 'innate-spells': { spellMode: 'innate', name: CoreUtils.i18n('tokenActionHud.dnd5e.innateSpells') },
- 'pact-spells': { spellMode: 'pact', name: CoreUtils.i18n('tokenActionHud.dnd5e.pactSpells') }
- }
+ const subcategoryMappings = {
+ '1st-level-spells': { spellMode: 1, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.1stLevelSpells') },
+ '2nd-level-spells': { spellMode: 2, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.2ndLevelSpells') },
+ '3rd-level-spells': { spellMode: 3, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.3rdLevelSpells') },
+ '4th-level-spells': { spellMode: 4, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.4thLevelSpells') },
+ '5th-level-spells': { spellMode: 5, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.5thLevelSpells') },
+ '6th-level-spells': { spellMode: 6, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.6thLevelSpells') },
+ '7th-level-spells': { spellMode: 7, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.7thLevelSpells') },
+ '8th-level-spells': { spellMode: 8, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.8thLevelSpells') },
+ '9th-level-spells': { spellMode: 9, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.9thLevelSpells') },
+ 'at-will-spells': { spellMode: 'atwill', name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.atWillSpells') },
+ cantrips: { spellMode: 0, name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.cantrips') },
+ 'innate-spells': { spellMode: 'innate', name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.innateSpells') },
+ 'pact-spells': { spellMode: 'pact', name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.pactSpells') }
+ }
- const spellSlotModes = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'pact']
+ const spellSlotModes = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'pact']
- for (const subcategoryId of this.spellSubcategoryIds) {
- const spellMode = subcategoryMappings[subcategoryId].spellMode
- const subcategoryName = subcategoryMappings[subcategoryId].name
+ for (const subcategoryId of this.spellSubcategoryIds) {
+ const spellMode = subcategoryMappings[subcategoryId].spellMode
+ const subcategoryName = subcategoryMappings[subcategoryId].name
- // Skip if no spells exist
- if (!spellsMap.has(subcategoryId)) continue
+ // Skip if no spells exist
+ if (!spellsMap.has(subcategoryId)) continue
- const levelInfo = (spellMode === 'pact') ? pactSlot[1] : spellSlots.find(spellSlot => spellSlot[0] === `spell${spellMode}`)?.[1]
- const slots = levelInfo?.value
- const max = levelInfo?.max
- const slotsAvailable = levelInfo?.slotAvailable
+ const levelInfo = (spellMode === 'pact') ? pactSlot[1] : spellSlots.find(spellSlot => spellSlot[0] === `spell${spellMode}`)?.[1]
+ const slots = levelInfo?.value
+ const max = levelInfo?.max
+ const slotsAvailable = levelInfo?.slotAvailable
- // Skip if spells require spell slots and none are available
- if (!slotsAvailable && spellSlotModes.includes(spellMode)) continue
+ // Skip if spells require spell slots and none are available
+ if (!slotsAvailable && spellSlotModes.includes(spellMode)) continue
- // Create subcategory data=
- const subcategoryInfo = {}
- subcategoryInfo.info1 = { class: 'tah-spotlight', text: (max >= 0) ? `${slots}/${max}` : '' }
- const subcategoryData = {
- id: subcategoryId,
- name: subcategoryName,
- type: 'system',
- info: subcategoryInfo
- }
+ // Create subcategory data=
+ const subcategoryInfo = {}
+ subcategoryInfo.info1 = { class: 'tah-spotlight', text: (max >= 0) ? `${slots}/${max}` : '' }
+ const subcategoryData = {
+ id: subcategoryId,
+ name: subcategoryName,
+ type: 'system',
+ info: subcategoryInfo
+ }
- // Add spell slot info to subcategory
- this.addSubcategoryInfo(subcategoryData)
+ // Add spell slot info to subcategory
+ this.addSubcategoryInfo(subcategoryData)
- const spells = spellsMap.get(subcategoryId)
+ const spells = spellsMap.get(subcategoryId)
- // Build actions
- this._buildActions(spells, subcategoryData, actionType)
+ // Build actions
+ this._buildActions(spells, subcategoryData, actionType)
- // Build activations
- if (this.activationSubcategoryIds) { this.buildActivations(spells, subcategoryData, actionType) }
+ // Build activations
+ if (this.activationSubcategoryIds) { this.buildActivations(spells, subcategoryData, actionType) }
+ }
}
- }
-
- /**
- * Build Utility
- * @private
- */
- _buildUtility () {
- // Exit if no subcategory exists
- if (!this.subcategoryIds.includes('utility')) return
+ /**
+ * Build Utility
+ * @private
+ */
+ _buildUtility () {
// Exit if every actor is not the character type
- if (!this.actors.every((actor) => actor.type === 'character')) return
+ if (!this.actors.every((actor) => actor.type === 'character')) return
- const actionType = 'utility'
+ const actionType = 'utility'
- // Set utility types
- const utilityTypes = {
- deathSave: { name: CoreUtils.i18n('DND5E.DeathSave') },
- inspiration: { name: CoreUtils.i18n('DND5E.Inspiration') }
- }
-
- // Delete 'deathSave' for multiple tokens
- if (this.actorId === 'multi' || this.actor.system.attributes.hp.value > 0) delete utilityTypes.deathSave
+ // Set utility types
+ const utilityTypes = {
+ deathSave: { name: coreModule.api.Utils.i18n('DND5E.DeathSave') },
+ inspiration: { name: coreModule.api.Utils.i18n('DND5E.Inspiration') }
+ }
- // Get actions
- const actions = Object.entries(utilityTypes)
- .map((utilityType) => {
- const id = utilityType[0]
- const name = utilityType[1].name
- const encodedValue = [actionType, this.actorId, this.tokenId, id].join(this.delimiter)
- let cssClass = ''
- if (utilityType[0] === 'inspiration') {
- const active = this.actors.every((actor) => actor.system.attributes?.inspiration)
- ? ' active'
- : ''
- cssClass = `toggle${active}`
- }
- return {
- id,
- name,
- encodedValue,
- cssClass,
- selected: true
- }
- })
+ // Delete 'deathSave' for multiple tokens
+ if (!this.actor || this.actor.system.attributes.hp.value > 0) delete utilityTypes.deathSave
+
+ // Get actions
+ const actions = Object.entries(utilityTypes)
+ .map((utilityType) => {
+ const id = utilityType[0]
+ const name = utilityType[1].name
+ const actionTypeName = `${coreModule.api.Utils.i18n(ACTION_TYPE[actionType])}: ` ?? ''
+ const listName = `${actionTypeName}${name}`
+ const encodedValue = [actionType, id].join(this.delimiter)
+ let cssClass = ''
+ if (utilityType[0] === 'inspiration') {
+ const active = this.actors.every((actor) => actor.system.attributes?.inspiration)
+ ? ' active'
+ : ''
+ cssClass = `toggle${active}`
+ }
+ return {
+ id,
+ name,
+ encodedValue,
+ cssClass,
+ listName
+ }
+ })
- // Crreate subcategory data
- const subcategoryData = { id: 'utility', type: 'system' }
+ // Crreate subcategory data
+ const subcategoryData = { id: 'utility', type: 'system' }
- // Add actions to action list
- this.addActionsToActionList(actions, subcategoryData)
- }
+ // Add actions to action list
+ this.addActionsToActionList(actions, subcategoryData)
+ }
- /**
- * Get Actions
- * @private
- * @param {object} items
- * @param {object} subcategoryData
- * @param {string} actionType
- */
- _buildActions (items, subcategoryData, actionType = 'item') {
+ /**
+ * Get Actions
+ * @private
+ * @param {object} items
+ * @param {object} subcategoryData
+ * @param {string} actionType
+ */
+ _buildActions (items, subcategoryData, actionType = 'item') {
// Exit if there are no items
- if (items.size === 0) return
+ if (items.size === 0) return
- // Exit if there is no subcategoryId
- const subcategoryId = (typeof subcategoryData === 'string' ? subcategoryData : subcategoryData?.id)
- if (!subcategoryId) return
+ // Exit if there is no subcategoryId
+ const subcategoryId = (typeof subcategoryData === 'string' ? subcategoryData : subcategoryData?.id)
+ if (!subcategoryId) return
- // Get actions
- const actions = [...items].map(item => this._getAction(actionType, item[1]))
+ // Get actions
+ const actions = [...items].map(item => this._getAction(actionType, item[1]))
- // Add actions to action list
- this.addActionsToActionList(actions, subcategoryData)
- }
+ // Add actions to action list
+ this.addActionsToActionList(actions, subcategoryData)
+ }
- /**
- * Get Action
- * @private
- * @param {string} actionType
- * @param {object} entity
- * @returns {object}
- */
- _getAction (actionType, entity) {
- const id = entity.id ?? entity._id
- let name = entity?.name ?? entity?.label
- if (
- entity?.system?.recharge &&
+ /**
+ * Get Action
+ * @private
+ * @param {string} actionType
+ * @param {object} entity
+ * @returns {object}
+ */
+ _getAction (actionType, entity) {
+ const id = entity.id ?? entity._id
+ let name = entity?.name ?? entity?.label
+ if (
+ entity?.system?.recharge &&
!entity?.system?.recharge?.charged &&
entity?.system?.recharge?.value
- ) {
- name += ` (${CoreUtils.i18n('DND5E.Recharge')})`
+ ) {
+ name += ` (${coreModule.api.Utils.i18n('DND5E.Recharge')})`
+ }
+ const actionTypeName = `${coreModule.api.Utils.i18n(ACTION_TYPE[actionType])}: ` ?? ''
+ const listName = `${actionTypeName}${name}`
+ let cssClass = ''
+ if (Object.hasOwn(entity, 'disabled')) {
+ const active = (!entity.disabled) ? ' active' : ''
+ cssClass = `toggle${active}`
+ }
+ const encodedValue = [actionType, id].join(this.delimiter)
+ const img = coreModule.api.Utils.getImage(entity)
+ const icon1 = this._getActivationTypeIcon(entity?.system?.activation?.type)
+ let icon2 = null
+ let info = null
+ if (entity.type === 'spell') {
+ icon2 = this._getPreparedIcon(entity)
+ if (this.displaySpellInfo) info = this._getSpellInfo(entity)
+ } else {
+ info = this._getItemInfo(entity)
+ }
+ const info1 = info?.info1
+ const info2 = info?.info2
+ const info3 = info?.info3
+ return {
+ id,
+ name,
+ encodedValue,
+ cssClass,
+ img,
+ icon1,
+ icon2,
+ info1,
+ info2,
+ info3,
+ listName
+ }
}
- let cssClass = ''
- if (Object.hasOwn(entity, 'disabled')) {
- const active = (!entity.disabled) ? ' active' : ''
- cssClass = `toggle${active}`
+
+ /**
+ * Is Active Item
+ * @param {object} item
+ * @returns {boolean}
+ */
+ _isActiveItem (item) {
+ if (this.showItemsWithoutActivationCosts) return true
+ const activationTypes = Object.keys(game.dnd5e.config.abilityActivationTypes).filter((at) => at !== 'none')
+ const activation = item.system.activation
+ const activationType = activation?.type
+ if (activation && activationTypes.includes(activationType)) return true
+ return false
}
- const encodedValue = [actionType, this.actorId, this.tokenId, id].join(this.delimiter)
- const img = CoreUtils.getImage(entity)
- const icon1 = this._getActivationTypeIcon(entity?.system?.activation?.type)
- let icon2 = null
- let info = null
- if (entity.type === 'spell') {
- icon2 = this._getPreparedIcon(entity)
- if (this.displaySpellInfo) info = this._getSpellInfo(entity)
- } else {
- info = this._getItemInfo(entity)
+
+ /**
+ * Is Equipped Item
+ * @private
+ * @param {object} item
+ * @returns {boolean}
+ */
+ _isEquippedItem (item) {
+ const type = item.type
+ const excludedTypes = ['consumable', 'spell', 'feat']
+ if (this.showUnequippedItems && !excludedTypes.includes(type)) return true
+ const equipped = item.system.equipped
+ if (equipped && type !== 'consumable') return true
+ return false
}
- const info1 = info?.info1
- const info2 = info?.info2
- const info3 = info?.info3
- return {
- id,
- name,
- encodedValue,
- cssClass,
- img,
- icon1,
- icon2,
- info1,
- info2,
- info3,
- selected: true
+
+ /**
+ * Is Usable Item
+ * @private
+ * @param {object} item The item
+ * @returns {boolean}
+ */
+ _isUsableItem (item) {
+ if (this.showUnchargedItems) return true
+ const uses = item.system.uses
+ if (!uses) return false
+ return true
}
- }
- /**
- * Is Active Item
- * @param {object} item
- * @returns {boolean}
- */
- _isActiveItem (item) {
- if (this.showItemsWithoutActivationCosts) return true
- const activationTypes = Object.keys(game.dnd5e.config.abilityActivationTypes).filter((at) => at !== 'none')
- const activation = item.system.activation
- const activationType = activation?.type
- if (activation && activationTypes.includes(activationType)) return true
- return false
- }
+ /**
+ * Is Usable Spell
+ * @param {object} spell The spell
+ * @returns {boolean}
+ */
+ _isUsableSpell (spell) {
+ if (this.actorType !== 'character' && this.showUnequippedItems) return true
+ const prepared = spell.system.preparation.prepared
+ if (this.showUnpreparedSpells) return true
+ // Set variables
+ const level = spell.system.level
+ const preparationModes = Object.keys(game.dnd5e.config.spellPreparationModes)
+ .filter(preparationMode => preparationMode !== 'prepared')
+ const preparationMode = spell.system.preparation.mode
+
+ // Return true if spell is a cantrip, has a preparation mode other than 'prepared' or is prepared
+ if (level === 0 || preparationModes.includes(preparationMode) || prepared) return true
+ return false
+ }
- /**
- * Is Equipped Item
- * @private
- * @param {object} item
- * @returns {boolean}
- */
- _isEquippedItem (item) {
- const type = item.type
- const excludedTypes = ['consumable', 'spell', 'feat']
- if (this.showUnequippedItems && !excludedTypes.includes(type)) return true
- const equipped = item.system.equipped
- if (equipped && type !== 'consumable') return true
- return false
- }
+ /**
+ * Get Item Info
+ * @private
+ * @param {object} item
+ * @returns {object}
+ */
+ _getItemInfo (item) {
+ const quantityData = this._getQuantityData(item)
+ const usesData = this._getUsesData(item)
+ const consumeData = this._getConsumeData(item)
- /**
- * Is Usable Item
- * @private
- * @param {object} item The item
- * @returns {boolean}
- */
- _isUsableItem (item) {
- if (this.showUnchargedItems) return true
- const uses = item.system.uses
- if (!uses) return false
- return true
- }
+ return {
+ info1: { text: quantityData },
+ info2: { text: usesData },
+ info3: { text: consumeData }
+ }
+ }
- /**
- * Is Usable Spell
- * @param {object} spell The spell
- * @returns {boolean}
- */
- _isUsableSpell (spell) {
- if (this.actorType !== 'character' && this.showUnequippedItems) return true
- const prepared = spell.system.preparation.prepared
- if (this.showUnpreparedSpells) return true
- // Set variables
- const level = spell.system.level
- const preparationModes = Object.keys(game.dnd5e.config.spellPreparationModes)
- .filter(preparationMode => preparationMode !== 'prepared')
- const preparationMode = spell.system.preparation.mode
-
- // Return true if spell is a cantrip, has a preparation mode other than 'prepared' or is prepared
- if (level === 0 || preparationModes.includes(preparationMode) || prepared) return true
- return false
- }
+ /**
+ * Add Spell Info
+ * @param {object} spell
+ */
+ _getSpellInfo (spell) {
+ const components = spell.system.components
- /**
- * Get Item Info
- * @private
- * @param {object} item
- * @returns {object}
- */
- _getItemInfo (item) {
- const quantityData = this._getQuantityData(item)
- const usesData = this._getUsesData(item)
- const consumeData = this._getConsumeData(item)
-
- return {
- info1: { text: quantityData },
- info2: { text: usesData },
- info3: { text: consumeData }
- }
- }
+ const componentsArray = []
+ const info1 = {}
+ const info2 = {}
+ const info3 = {}
- /**
- * Add Spell Info
- * @param {object} spell
- */
- _getSpellInfo (spell) {
- const components = spell.system.components
-
- const componentsArray = []
- const info1 = {}
- const info2 = {}
- const info3 = {}
-
- // Components
- if (components?.vocal) componentsArray.push(CoreUtils.i18n('DND5E.ComponentVerbal'))
- if (components?.somatic) componentsArray.push(CoreUtils.i18n('DND5E.ComponentSomatic'))
- if (components?.material) componentsArray.push(CoreUtils.i18n('DND5E.ComponentMaterial'))
-
- if (componentsArray.length) {
- info1.title = componentsArray.join(', ')
- info1.text = componentsArray.map(component => component.charAt(0).toUpperCase()).join('')
- }
+ // Components
+ if (components?.vocal) componentsArray.push(coreModule.api.Utils.i18n('DND5E.ComponentVerbal'))
+ if (components?.somatic) componentsArray.push(coreModule.api.Utils.i18n('DND5E.ComponentSomatic'))
+ if (components?.material) componentsArray.push(coreModule.api.Utils.i18n('DND5E.ComponentMaterial'))
- // Concentration
- if (components?.concentration) {
- const title = CoreUtils.i18n('DND5E.Concentration')
- info2.title = title
- info2.text = title.charAt(0).toUpperCase()
- }
+ if (componentsArray.length) {
+ info1.title = componentsArray.join(', ')
+ info1.text = componentsArray.map(component => component.charAt(0).toUpperCase()).join('')
+ }
+
+ // Concentration
+ if (components?.concentration) {
+ const title = coreModule.api.Utils.i18n('DND5E.Concentration')
+ info2.title = title
+ info2.text = title.charAt(0).toUpperCase()
+ }
- // Ritual
- if (components?.ritual) {
- const title = CoreUtils.i18n('DND5E.Ritual')
- info3.title = title
- info3.text = title.charAt(0).toUpperCase()
+ // Ritual
+ if (components?.ritual) {
+ const title = coreModule.api.Utils.i18n('DND5E.Ritual')
+ info3.title = title
+ info3.text = title.charAt(0).toUpperCase()
+ }
+
+ return { info1, info2, info3 }
}
- return { info1, info2, info3 }
- }
+ /**
+ * Get Actors
+ * @private
+ * @returns {object}
+ */
+ _getActors () {
+ const allowedTypes = ['character', 'npc']
+ const actors = canvas.tokens.controlled.filter(token => token.actor).map((token) => token.actor)
+ if (actors.every((actor) => allowedTypes.includes(actor.type))) { return actors }
+ }
- /**
- * Get Actors
- * @private
- * @returns {object}
- */
- _getActors () {
- const allowedTypes = ['character', 'npc']
- const actors = canvas.tokens.controlled.map((token) => token.actor)
- if (actors.every((actor) => allowedTypes.includes(actor.type))) { return actors }
- }
+ /**
+ * Get Actors
+ * @private
+ * @returns {object}
+ */
+ _getTokens () {
+ const allowedTypes = ['character', 'npc']
+ const tokens = canvas.tokens.controlled
+ const actors = tokens.filter(token => token.actor).map((token) => token.actor)
+ if (actors.every((actor) => allowedTypes.includes(actor.type))) { return tokens }
+ }
- /**
- * Get Quantity
- * @private
- * @param {object} item
- * @returns {string}
- */
- _getQuantityData (item) {
- const quantity = item?.system?.quantity ?? 0
- return (quantity > 1) ? quantity : ''
- }
+ /**
+ * Get Quantity
+ * @private
+ * @param {object} item
+ * @returns {string}
+ */
+ _getQuantityData (item) {
+ const quantity = item?.system?.quantity ?? 0
+ return (quantity > 1) ? quantity : ''
+ }
- /**
- * Get Uses
- * @private
- * @param {object} item
- * @returns {string}
- */
- _getUsesData (item) {
- const uses = item?.system?.uses
- if (!uses) return ''
- return (uses.value > 0 || uses.max > 0) ? `${uses.value ?? '0'}${(uses.max > 0) ? `/${uses.max}` : ''}` : ''
- }
+ /**
+ * Get Uses
+ * @private
+ * @param {object} item
+ * @returns {string}
+ */
+ _getUsesData (item) {
+ const uses = item?.system?.uses
+ if (!uses) return ''
+ return (uses.value > 0 || uses.max > 0) ? `${uses.value ?? '0'}${(uses.max > 0) ? `/${uses.max}` : ''}` : ''
+ }
- /**
- * Get Consume
- * @private
- * @param {object} item
- * @param {object} actor
- * @returns {string}
- */
- _getConsumeData (item) {
+ /**
+ * Get Consume
+ * @private
+ * @param {object} item
+ * @param {object} actor
+ * @returns {string}
+ */
+ _getConsumeData (item) {
// Get consume target and type
- const consumeId = item?.system?.consume?.target
- const consumeType = item?.system?.consume?.type
+ const consumeId = item?.system?.consume?.target
+ const consumeType = item?.system?.consume?.type
- // Return resources
- if (consumeType === 'attribute') {
- const parentId = consumeId.substr(0, consumeId.lastIndexOf('.'))
- const target = this.actor.system[parentId]
+ // Return resources
+ if (consumeType === 'attribute') {
+ const parentId = consumeId.substr(0, consumeId.lastIndexOf('.'))
+ const target = this.actor.system[parentId]
- return (target) ? `${target.value ?? '0'}${(target.max) ? `/${target.max}` : ''}` : ''
- }
+ return (target) ? `${target.value ?? '0'}${(target.max) ? `/${target.max}` : ''}` : ''
+ }
- const target = this.items.get(consumeId)
+ const target = this.items.get(consumeId)
- // Return charges
- if (consumeType === 'charges') {
- const uses = target?.system.uses
+ // Return charges
+ if (consumeType === 'charges') {
+ const uses = target?.system.uses
- return (uses?.value) ? `${uses.value}${(uses.max) ? `/${uses.max}` : ''}` : ''
- }
+ return (uses?.value) ? `${uses.value}${(uses.max) ? `/${uses.max}` : ''}` : ''
+ }
- // Return quantity
- return target?.system?.quantity ?? ''
- }
+ // Return quantity
+ return target?.system?.quantity ?? ''
+ }
- /**
- * Discard Slow Items
- * @private
- * @param {object} items
- * @returns {object}
- */
- _discardSlowItems (items) {
+ /**
+ * Discard Slow Items
+ * @private
+ * @param {object} items
+ * @returns {object}
+ */
+ _discardSlowItems (items) {
// Get setting
- const showSlowActions = Utils.getSetting('showSlowActions')
+ const showSlowActions = Utils.getSetting('showSlowActions')
- // Return all items
- if (showSlowActions) return items
+ // Return all items
+ if (showSlowActions) return items
- // Return filtered items
- const slowActivationTypes = ['minute', 'hour', 'day']
+ // Return filtered items
+ const slowActivationTypes = ['minute', 'hour', 'day']
- // Initialize map
- const filteredItems = new Map()
+ // Initialize map
+ const filteredItems = new Map()
- // Loop items and set those with fast activation types into the new map
- for (const [key, value] of items.entries()) {
- const activation = value.system.activation
- const activationType = value.system.activation?.type
- if (activation && !slowActivationTypes.includes(activationType)) filteredItems.set(key, value)
- }
+ // Loop items and set those with fast activation types into the new map
+ for (const [key, value] of items.entries()) {
+ const activation = value.system.activation
+ const activationType = value.system.activation?.type
+ if (activation && !slowActivationTypes.includes(activationType)) filteredItems.set(key, value)
+ }
- return filteredItems
- }
+ return filteredItems
+ }
- /**
- * Get Proficiency Icon
- * @param {string} level
- * @returns {string}
- */
- _getProficiencyIcon (level) {
- const title = CONFIG.DND5E.proficiencyLevels[level] ?? ''
- const icon = PROFICIENCY_LEVEL_ICON[level]
- if (icon) return ``
- }
+ /**
+ * Get Proficiency Icon
+ * @param {string} level
+ * @returns {string}
+ */
+ _getProficiencyIcon (level) {
+ const title = CONFIG.DND5E.proficiencyLevels[level] ?? ''
+ const icon = PROFICIENCY_LEVEL_ICON[level]
+ if (icon) return ``
+ }
- /**
- * Get icon for the activation type
- * @param {object} activationType
- * @returns {string}
- */
- _getActivationTypeIcon (activationType) {
- const title = CONFIG.DND5E.abilityActivationTypes[activationType] ?? ''
- const icon = ACTIVATION_TYPE_ICON[activationType]
- if (icon) return ``
- }
+ /**
+ * Get icon for the activation type
+ * @param {object} activationType
+ * @returns {string}
+ */
+ _getActivationTypeIcon (activationType) {
+ const title = CONFIG.DND5E.abilityActivationTypes[activationType] ?? ''
+ const icon = ACTIVATION_TYPE_ICON[activationType]
+ if (icon) return ``
+ }
- /**
- * Get icon for a prepared spell
- * @param {boolean} prepararation
- * @returns
- */
- _getPreparedIcon (spell) {
- const level = spell.system.level
- const preparationMode = spell.system.preparation.mode
- const prepared = spell.system.preparation.prepared
- const icon = (prepared) ? PREPARED_ICON : `${PREPARED_ICON} tah-icon-disabled`
- const title = (prepared) ? CoreUtils.i18n('DND5E.SpellPrepared') : CoreUtils.i18n('DND5E.SpellUnprepared')
-
- // Return icon if the preparation mode is 'prepared' and the spell is not a cantrip
- return (preparationMode === 'prepared' && level !== 0) ? `` : ''
+ /**
+ * Get icon for a prepared spell
+ * @param {boolean} prepararation
+ * @returns
+ */
+ _getPreparedIcon (spell) {
+ const level = spell.system.level
+ const preparationMode = spell.system.preparation.mode
+ const prepared = spell.system.preparation.prepared
+ const icon = (prepared) ? PREPARED_ICON : `${PREPARED_ICON} tah-icon-disabled`
+ const title = (prepared) ? coreModule.api.Utils.i18n('DND5E.SpellPrepared') : coreModule.api.Utils.i18n('DND5E.SpellUnprepared')
+
+ // Return icon if the preparation mode is 'prepared' and the spell is not a cantrip
+ return (preparationMode === 'prepared' && level !== 0) ? `` : ''
+ }
}
-}
+})
diff --git a/scripts/config.js b/scripts/config.js
deleted file mode 100644
index 1a47606..0000000
--- a/scripts/config.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// For distribution
-const coreModulePath = '../../token-action-hud-core/scripts/token-action-hud-core.min.js'
-const coreModule = await import(coreModulePath)
-export const CoreActionHandler = coreModule.ActionHandler
-export const CoreActionListExtender = coreModule.ActionListExtender
-export const CoreCategoryManager = coreModule.CategoryManager
-export const CorePreRollHandler = coreModule.PreRollHandler
-export const CoreRollHandler = coreModule.RollHandler
-export const CoreSystemManager = coreModule.SystemManager
-export const CoreUtils = coreModule.Utils
-export const Logger = coreModule.Logger
-
-// For development
-/* const coreModulePath = '../../token-action-hud-core/'
-const coreActionHandlerFile = `${coreModulePath}scripts/action-handlers/action-handler.js`
-const coreActionListExtenderFile = `${coreModulePath}scripts/action-handlers/action-list-extender.js`
-const coreCategoryManagerFile = `${coreModulePath}scripts/category-manager.js`
-const corePreRollHandlerFile = `${coreModulePath}scripts/roll-handlers/pre-roll-handler.js`
-const coreRollHandlerFile = `${coreModulePath}scripts/roll-handlers/roll-handler.js`
-const coreSystemManagerFile = `${coreModulePath}scripts/system-manager.js`
-const coreUtilsFile = `${coreModulePath}scripts/utilities/utils.js`
-
-export const CoreActionHandler = await import(coreActionHandlerFile).then(module => module.ActionHandler)
-export const CoreActionListExtender = await import(coreActionListExtenderFile).then(module => module.ActionListExtender)
-export const CoreCategoryManager = await import(coreCategoryManagerFile).then(module => module.CategoryManager)
-export const CorePreRollHandler = await import(corePreRollHandlerFile).then(module => module.PreRollHandler)
-export const CoreRollHandler = await import(coreRollHandlerFile).then(module => module.RollHandler)
-export const CoreSystemManager = await import(coreSystemManagerFile).then(module => module.SystemManager)
-const coreUtilsModule = await import(coreUtilsFile)
-export const CoreUtils = coreUtilsModule.Utils
-export const Logger = coreUtilsModule.Logger */
diff --git a/scripts/constants.js b/scripts/constants.js
index 6e58e6e..d5f849f 100644
--- a/scripts/constants.js
+++ b/scripts/constants.js
@@ -15,7 +15,23 @@ export const CORE_MODULE = {
/**
* Core module version required by the system module
*/
-export const REQUIRED_CORE_MODULE_VERSION = '1.2'
+export const REQUIRED_CORE_MODULE_VERSION = '1.3'
+
+/**
+ * Action type
+ */
+export const ACTION_TYPE = {
+ ability: 'DND5E.Ability',
+ check: 'tokenActionHud.dnd5e.check',
+ condition: 'tokenActionHud.dnd5e.condition',
+ effect: 'DND5E.Effect',
+ feature: 'ITEM.TypeFeat',
+ item: 'tokenActionHud.dnd5e.item',
+ save: 'DND5E.ActionSave',
+ skill: 'tokenActionHud.dnd5e.skill',
+ spell: 'ITEM.TypeSpell',
+ utility: 'DND5E.ActionUtil'
+}
/**
* Activation type icons
@@ -55,3 +71,67 @@ export const PROFICIENCY_LEVEL_ICON = {
1: 'fas fa-check',
2: 'fas fa-check-double'
}
+
+export const SUBCATEGORY = {
+ _1stLevelSpells: { id: '1st-level-spells', name: 'tokenActionHud.dnd5e.1stLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _2ndLevelSpells: { id: '2nd-level-spells', name: 'tokenActionHud.dnd5e.2ndLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _3rdLevelSpells: { id: '3rd-level-spells', name: 'tokenActionHud.dnd5e.3rdLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _4thLevelSpells: { id: '4th-level-spells', name: 'tokenActionHud.dnd5e.4thLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _5thLevelSpells: { id: '5th-level-spells', name: 'tokenActionHud.dnd5e.5thLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _6thLevelSpells: { id: '6th-level-spells', name: 'tokenActionHud.dnd5e.6thLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _7thLevelSpells: { id: '7th-level-spells', name: 'tokenActionHud.dnd5e.7thLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _8thLevelSpells: { id: '8th-level-spells', name: 'tokenActionHud.dnd5e.8thLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ _9thLevelSpells: { id: '9th-level-spells', name: 'tokenActionHud.dnd5e.9thLevelSpells', type: 'system', hasDerivedSubcategories: false },
+ abilities: { id: 'abilities', name: 'tokenActionHud.dnd5e.abilities', type: 'system', hasDerivedSubcategories: false },
+ actions: { id: 'actions', name: 'DND5E.ActionPl', type: 'system', hasDerivedSubcategories: true },
+ activeFeatures: { id: 'active-features', name: 'tokenActionHud.dnd5e.activeFeatures', type: 'system', hasDerivedSubcategories: false },
+ artificerInfusions: { id: 'artificer-infusions', name: 'tokenActionHud.dnd5e.artificerInfusions', type: 'system', hasDerivedSubcategories: false },
+ atWillSpells: { id: 'at-will-spells', name: 'tokenActionHud.dnd5e.atWillSpells', type: 'system', hasDerivedSubcategories: false },
+ backgroundFeatures: { id: 'background-features', name: 'tokenActionHud.dnd5e.backgroundFeatures', type: 'system', hasDerivedSubcategories: false },
+ bonusActions: { id: 'bonus-actions', name: 'tokenActionHud.dnd5e.bonusActions', type: 'system', hasDerivedSubcategories: true },
+ cantrips: { id: 'cantrips', name: 'tokenActionHud.dnd5e.cantrips', type: 'system', hasDerivedSubcategories: false },
+ channelDivinity: { id: 'channel-divinity', name: 'tokenActionHud.dnd5e.channelDivinity', type: 'system', hasDerivedSubcategories: false },
+ checks: { id: 'checks', name: 'tokenActionHud.dnd5e.checks', type: 'system', hasDerivedSubcategories: false },
+ classFeatures: { id: 'class-features', name: 'tokenActionHud.dnd5e.classFeatures', type: 'system', hasDerivedSubcategories: false },
+ combat: { id: 'combat', name: 'tokenActionHud.combat', type: 'system', hasDerivedSubcategories: false },
+ conditions: { id: 'conditions', name: 'tokenActionHud.dnd5e.conditions', type: 'system', hasDerivedSubcategories: false },
+ consumables: { id: 'consumables', name: 'ITEM.TypeConsumablePl', type: 'system', hasDerivedSubcategories: false },
+ containers: { id: 'containers', name: 'ITEM.TypeContainerPl', type: 'system', hasDerivedSubcategories: false },
+ crewActions: { id: 'crew-actions', name: 'tokenActionHud.dnd5e.crewActions', type: 'system', hasDerivedSubcategories: true },
+ defensiveTactics: { id: 'defensive-tactics', name: 'tokenActionHud.dnd5e.defensiveTactics', type: 'system', hasDerivedSubcategories: false },
+ eldritchInvocations: { id: 'eldritch-invocations', name: 'tokenActionHud.dnd5e.eldritchInvocations', type: 'system', hasDerivedSubcategories: false },
+ elementalDisciplines: { id: 'elemental-disciplines', name: 'tokenActionHud.dnd5e.elementalDisciplines', type: 'system', hasDerivedSubcategories: false },
+ equipment: { id: 'equipment', name: 'ITEM.TypeEquipmentPl', type: 'system', hasDerivedSubcategories: false },
+ equipped: { id: 'equipped', name: 'DND5E.Equipped', type: 'system', hasDerivedSubcategories: false },
+ feats: { id: 'feats', name: 'tokenActionHud.dnd5e.feats', type: 'system', hasDerivedSubcategories: false },
+ fightingStyles: { id: 'fighting-styles', name: 'tokenActionHud.dnd5e.fightingStyles', type: 'system', hasDerivedSubcategories: false },
+ huntersPrey: { id: 'hunters-prey', name: 'tokenActionHud.dnd5e.huntersPrey', type: 'system', hasDerivedSubcategories: false },
+ innateSpells: { id: 'innate-spells', name: 'tokenActionHud.dnd5e.innateSpells', type: 'system', hasDerivedSubcategories: false },
+ kiAbilities: { id: 'ki-abilities', name: 'tokenActionHud.dnd5e.kiAbilities', type: 'system', hasDerivedSubcategories: false },
+ lairActions: { id: 'lair-actions', name: 'tokenActionHud.dnd5e.lairActions', type: 'system', hasDerivedSubcategories: true },
+ legendaryActions: { id: 'legendary-actions', name: 'tokenActionHud.dnd5e.legendaryActions', type: 'system', hasDerivedSubcategories: true },
+ loot: { id: 'loot', name: 'ITEM.TypeLootPl', type: 'system', hasDerivedSubcategories: false },
+ maneuvers: { id: 'maneuvers', name: 'tokenActionHud.dnd5e.maneuvers', type: 'system', hasDerivedSubcategories: false },
+ metamagicOptions: { id: 'metamagic-options', name: 'tokenActionHud.dnd5e.metamagicOptions', type: 'system', hasDerivedSubcategories: false },
+ monsterFeatures: { id: 'monster-features', name: 'tokenActionHud.dnd5e.monsterFeatures', type: 'system', hasDerivedSubcategories: false },
+ multiattacks: { id: 'multiattacks', name: 'tokenActionHud.dnd5e.multiattacks', type: 'system', hasDerivedSubcategories: false },
+ otherActions: { id: 'other-actions', name: 'tokenActionHud.dnd5e.otherActions', type: 'system', hasDerivedSubcategories: true },
+ pactBoons: { id: 'pact-boons', name: 'tokenActionHud.dnd5e.pactBoons', type: 'system', hasDerivedSubcategories: false },
+ pactSpells: { id: 'pact-spells', name: 'tokenActionHud.dnd5e.pactSpells', type: 'system', hasDerivedSubcategories: false },
+ passiveEffects: { id: 'passive-effects', name: 'DND5E.EffectPassive', type: 'system', hasDerivedSubcategories: false },
+ passiveFeatures: { id: 'passive-features', name: 'tokenActionHud.dnd5e.passiveFeatures', type: 'system', hasDerivedSubcategories: false },
+ psionicPowers: { id: 'psionic-powers', name: 'tokenActionHud.dnd5e.psionicPowers', type: 'system', hasDerivedSubcategories: false },
+ raceFeatures: { id: 'race-features', name: 'tokenActionHud.dnd5e.raceFeatures', type: 'system', hasDerivedSubcategories: false },
+ reactions: { id: 'reactions', name: 'DND5E.ReactionPl', type: 'system', hasDerivedSubcategories: true },
+ rests: { id: 'rests', name: 'tokenActionHud.dnd5e.rests', type: 'system', hasDerivedSubcategories: false },
+ runes: { id: 'runes', name: 'tokenActionHud.dnd5e.runes', type: 'system', hasDerivedSubcategories: false },
+ saves: { id: 'saves', name: 'DND5E.ClassSaves', type: 'system', hasDerivedSubcategories: false },
+ skills: { id: 'skills', name: 'tokenActionHud.dnd5e.skills', type: 'system', hasDerivedSubcategories: false },
+ superiorHuntersDefense: { id: 'superior-hunters-defense', name: 'tokenActionHud.dnd5e.superiorHuntersDefense', type: 'system', hasDerivedSubcategories: false },
+ temporaryEffects: { id: 'temporary-effects', name: 'DND5E.EffectTemporary', type: 'system', hasDerivedSubcategories: false },
+ token: { id: 'token', name: 'tokenActionHud.token', type: 'system', hasDerivedSubcategories: false },
+ tools: { id: 'tools', name: 'ITEM.TypeToolPl', type: 'system', hasDerivedSubcategories: false },
+ unequipped: { id: 'unequipped', name: 'DND5E.Unequipped', type: 'system', hasDerivedSubcategories: false },
+ utility: { id: 'utility', name: 'tokenActionHud.utility', type: 'system', hasDerivedSubcategories: false },
+ weapons: { id: 'weapons', name: 'ITEM.TypeWeaponPl', type: 'system', hasDerivedSubcategories: false }
+}
diff --git a/scripts/defaults.js b/scripts/defaults.js
index 41c7271..a606d20 100644
--- a/scripts/defaults.js
+++ b/scripts/defaults.js
@@ -1,333 +1,99 @@
+import { SUBCATEGORY } from './constants.js'
+
/**
* Default categories and subcategories
*/
export let DEFAULTS = null
-Hooks.on('i18nInit', async () => {
+Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
+ const subcategories = SUBCATEGORY
+ Object.values(subcategories).forEach(subcategory => {
+ subcategory.name = coreModule.api.Utils.i18n(subcategory.name)
+ subcategory.listName = `Subcategory: ${coreModule.api.Utils.i18n(subcategory.name)}`
+ })
+ const subcategoriesArray = Object.values(subcategories)
DEFAULTS = {
categories: [
{
nestId: 'inventory',
id: 'inventory',
- name: game.i18n.localize('DND5E.Inventory'),
+ name: coreModule.api.Utils.i18n('DND5E.Inventory'),
subcategories: [
- {
- nestId: 'inventory_weapons',
- id: 'weapons',
- name: game.i18n.localize('ITEM.TypeWeaponPl'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'inventory_equipment',
- id: 'equipment',
- name: game.i18n.localize('ITEM.TypeEquipmentPl'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'inventory_consumables',
- id: 'consumables',
- name: game.i18n.localize('ITEM.TypeConsumablePl'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'inventory_tools',
- id: 'tools',
- name: game.i18n.localize('ITEM.TypeToolPl'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'inventory_containers',
- id: 'containers',
- name: game.i18n.localize('ITEM.TypeContainerPl'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'inventory_loot',
- id: 'loot',
- name: game.i18n.localize('ITEM.TypeLootPl'),
- type: 'system',
- hasDerivedSubcategories: false
- }
+ { ...subcategories.weapons, nestId: 'inventory_weapons' },
+ { ...subcategories.equipment, nestId: 'inventory_equipment' },
+ { ...subcategories.consumables, nestId: 'inventory_consumables' },
+ { ...subcategories.tools, nestId: 'inventory_tools' },
+ { ...subcategories.containers, nestId: 'inventory_containers' },
+ { ...subcategories.loot, nestId: 'inventory_loot' }
]
},
{
nestId: 'features',
id: 'features',
- name: game.i18n.localize('DND5E.Features'),
+ name: coreModule.api.Utils.i18n('DND5E.Features'),
subcategories: [
- {
- nestId: 'features_active-features',
- id: 'active-features',
- name: game.i18n.localize('tokenActionHud.dnd5e.activeFeatures'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- id: 'passive-features',
- nestId: 'features_passive-features',
- name: game.i18n.localize('tokenActionHud.dnd5e.passiveFeatures'),
- type: 'system',
- hasDerivedSubcategories: false
- }
+ { ...subcategories.activeFeatures, nestId: 'features_active-features' },
+ { ...subcategories.passiveFeatures, nestId: 'features_passive-features' }
]
},
{
nestId: 'spells',
id: 'spells',
- name: game.i18n.localize('ITEM.TypeSpellPl'),
+ name: coreModule.api.Utils.i18n('ITEM.TypeSpellPl'),
subcategories: [
- {
- nestId: 'spells_at-will-spells',
- id: 'at-will-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.atWillSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_innate-spells',
- id: 'innate-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.innateSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_pact-spells',
- id: 'pact-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.pactSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_cantrips',
- id: 'cantrips',
- name: game.i18n.localize('tokenActionHud.dnd5e.cantrips'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_1st-level-spells',
- id: '1st-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.1stLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_2nd-level-spells',
- id: '2nd-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.2ndLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_3rd-level-spells',
- id: '3rd-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.3rdLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_4th-level-spells',
- id: '4th-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.4thLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_5th-level-spells',
- id: '5th-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.5thLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_6th-level-spells',
- id: '6th-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.6thLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_7th-level-spells',
- id: '7th-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.7thLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_8th-level-spells',
- id: '8th-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.8thLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'spells_9th-level-spells',
- id: '9th-level-spells',
- name: game.i18n.localize('tokenActionHud.dnd5e.9thLevelSpells'),
- type: 'system',
- hasDerivedSubcategories: false
- }
+ { ...subcategories.atWillSpells, nestId: 'spells_at-will-spells' },
+ { ...subcategories.innateSpells, nestId: 'spells_innate-spells' },
+ { ...subcategories.pactSpells, nestId: 'spells_pact-spells' },
+ { ...subcategories.cantrips, nestId: 'spells_cantrips' },
+ { ...subcategories._1stLevelSpells, nestId: 'spells_1st-level-spells' },
+ { ...subcategories._2ndLevelSpells, nestId: 'spells_2nd-level-spells' },
+ { ...subcategories._3rdLevelSpells, nestId: 'spells_3rd-level-spells' },
+ { ...subcategories._4thLevelSpells, nestId: 'spells_4th-level-spells' },
+ { ...subcategories._5thLevelSpells, nestId: 'spells_5th-level-spells' },
+ { ...subcategories._6thLevelSpells, nestId: 'spells_6th-level-spells' },
+ { ...subcategories._7thLevelSpells, nestId: 'spells_7th-level-spells' },
+ { ...subcategories._8thLevelSpells, nestId: 'spells_8th-level-spells' },
+ { ...subcategories._9thLevelSpells, nestId: 'spells_9th-level-spells' }
]
},
{
nestId: 'attributes',
id: 'attributes',
- name: game.i18n.localize('DND5E.Attributes'),
+ name: coreModule.api.Utils.i18n('DND5E.Attributes'),
subcategories: [
- {
- nestId: 'attributes_abilities',
- id: 'abilities',
- name: game.i18n.localize('tokenActionHud.dnd5e.abilities'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'attributes_skills',
- id: 'skills',
- name: game.i18n.localize('tokenActionHud.dnd5e.skills'),
- type: 'system',
- hasDerivedSubcategories: false
- }
+ { ...subcategories.abilities, nestId: 'attributes_abilities' },
+ { ...subcategories.skills, nestId: 'attributes_skills' }
]
},
{
nestId: 'effects',
id: 'effects',
- name: game.i18n.localize('DND5E.Effects'),
+ name: coreModule.api.Utils.i18n('DND5E.Effects'),
subcategories: [
- {
- nestId: 'effects_temporary-effects',
- id: 'temporary-effects',
- name: game.i18n.localize('DND5E.EffectTemporary'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'effects_passive-effects',
- id: 'passive-effects',
- name: game.i18n.localize('DND5E.EffectPassive'),
- type: 'system',
- hasDerivedSubcategories: false
- }
+ { ...subcategories.temporaryEffects, nestId: 'effects_temporary-effects' },
+ { ...subcategories.passiveEffects, nestId: 'effects_passive-effects' }
]
},
{
nestId: 'conditions',
id: 'conditions',
- name: game.i18n.localize('tokenActionHud.dnd5e.conditions'),
+ name: coreModule.api.Utils.i18n('tokenActionHud.dnd5e.conditions'),
subcategories: [
- {
- nestId: 'conditions_conditions',
- id: 'conditions',
- name: game.i18n.localize('tokenActionHud.dnd5e.conditions'),
- type: 'system',
- hasDerivedSubcategories: false
- }
+ { ...subcategories.conditions, nestId: 'conditions_conditions' }
]
},
{
nestId: 'utility',
id: 'utility',
- name: game.i18n.localize('tokenActionHud.utility'),
+ name: coreModule.api.Utils.i18n('tokenActionHud.utility'),
subcategories: [
- {
- nestId: 'utility_combat',
- id: 'combat',
- name: game.i18n.localize('tokenActionHud.combat'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'utility_token',
- id: 'token',
- name: game.i18n.localize('tokenActionHud.token'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'utility_rests',
- id: 'rests',
- name: game.i18n.localize('tokenActionHud.dnd5e.rests'),
- type: 'system',
- hasDerivedSubcategories: false
- },
- {
- nestId: 'utility_utility',
- id: 'utility',
- name: game.i18n.localize('tokenActionHud.utility'),
- type: 'system',
- hasDerivedSubcategories: false
- }
+ { ...subcategories.combat, nestId: 'utility_combat' },
+ { ...subcategories.token, nestId: 'utility_token' },
+ { ...subcategories.rests, nestId: 'utility_rests' },
+ { ...subcategories.utility, nestId: 'utility_utility' }
]
}
],
- subcategories: [
- { id: '1st-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.1stLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '2nd-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.2ndLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '3rd-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.3rdLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '4th-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.4thLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '5th-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.5thLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '6th-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.6thLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '7th-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.7thLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '8th-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.8thLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: '9th-level-spells', name: game.i18n.localize('tokenActionHud.dnd5e.9thLevelSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: 'abilities', name: game.i18n.localize('tokenActionHud.dnd5e.abilities'), type: 'system', hasDerivedSubcategories: false },
- { id: 'actions', name: game.i18n.localize('DND5E.ActionPl'), type: 'system', hasDerivedSubcategories: true },
- { id: 'active-features', name: game.i18n.localize('tokenActionHud.dnd5e.activeFeatures'), type: 'system', hasDerivedSubcategories: false },
- { id: 'artificer-infusions', name: game.i18n.localize('tokenActionHud.dnd5e.artificerInfusions'), type: 'system', hasDerivedSubcategories: false },
- { id: 'at-will-spells', name: game.i18n.localize('tokenActionHud.dnd5e.atWillSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: 'background-features', name: game.i18n.localize('tokenActionHud.dnd5e.backgroundFeatures'), type: 'system', hasDerivedSubcategories: false },
- { id: 'bonus-actions', name: game.i18n.localize('tokenActionHud.dnd5e.bonusActions'), type: 'system', hasDerivedSubcategories: true },
- { id: 'cantrips', name: game.i18n.localize('tokenActionHud.dnd5e.cantrips'), type: 'system', hasDerivedSubcategories: false },
- { id: 'channel-divinity', name: game.i18n.localize('tokenActionHud.dnd5e.channelDivinity'), type: 'system', hasDerivedSubcategories: false },
- { id: 'checks', name: game.i18n.localize('tokenActionHud.dnd5e.checks'), type: 'system', hasDerivedSubcategories: false },
- { id: 'class-features', name: game.i18n.localize('tokenActionHud.dnd5e.classFeatures'), type: 'system', hasDerivedSubcategories: false },
- { id: 'combat', name: game.i18n.localize('tokenActionHud.combat'), type: 'system', hasDerivedSubcategories: false },
- { id: 'conditions', name: game.i18n.localize('tokenActionHud.dnd5e.conditions'), type: 'system', hasDerivedSubcategories: false },
- { id: 'consumables', name: game.i18n.localize('ITEM.TypeConsumablePl'), type: 'system', hasDerivedSubcategories: false },
- { id: 'containers', name: game.i18n.localize('ITEM.TypeContainerPl'), type: 'system', hasDerivedSubcategories: false },
- { id: 'crew-actions', name: game.i18n.localize('tokenActionHud.dnd5e.crewActions'), type: 'system', hasDerivedSubcategories: true },
- { id: 'defensive-tactics', name: game.i18n.localize('tokenActionHud.dnd5e.defensiveTactics'), type: 'system', hasDerivedSubcategories: false },
- { id: 'eldritch-invocations', name: game.i18n.localize('tokenActionHud.dnd5e.eldritchInvocations'), type: 'system', hasDerivedSubcategories: false },
- { id: 'elemental-disciplines', name: game.i18n.localize('tokenActionHud.dnd5e.elementalDisciplines'), type: 'system', hasDerivedSubcategories: false },
- { id: 'equipment', name: game.i18n.localize('ITEM.TypeEquipmentPl'), type: 'system', hasDerivedSubcategories: false },
- { id: 'equipped', name: game.i18n.localize('DND5E.Equipped'), type: 'system', hasDerivedSubcategories: false },
- { id: 'feats', name: game.i18n.localize('tokenActionHud.dnd5e.feats'), type: 'system', hasDerivedSubcategories: false },
- { id: 'fighting-styles', name: game.i18n.localize('tokenActionHud.dnd5e.fightingStyles'), type: 'system', hasDerivedSubcategories: false },
- { id: 'hunters-prey', name: game.i18n.localize('tokenActionHud.dnd5e.huntersPrey'), type: 'system', hasDerivedSubcategories: false },
- { id: 'innate-spells', name: game.i18n.localize('tokenActionHud.dnd5e.innateSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: 'ki-abilities', name: game.i18n.localize('tokenActionHud.dnd5e.kiAbilities'), type: 'system', hasDerivedSubcategories: false },
- { id: 'lair-actions', name: game.i18n.localize('tokenActionHud.dnd5e.lairActions'), type: 'system', hasDerivedSubcategories: true },
- { id: 'legendary-actions', name: game.i18n.localize('tokenActionHud.dnd5e.legendaryActions'), type: 'system', hasDerivedSubcategories: true },
- { id: 'loot', name: game.i18n.localize('ITEM.TypeLootPl'), type: 'system', hasDerivedSubcategories: false },
- { id: 'maneuvers', name: game.i18n.localize('tokenActionHud.dnd5e.maneuvers'), type: 'system', hasDerivedSubcategories: false },
- { id: 'metamagic-options', name: game.i18n.localize('tokenActionHud.dnd5e.metamagicOptions'), type: 'system', hasDerivedSubcategories: false },
- { id: 'monster-features', name: game.i18n.localize('tokenActionHud.dnd5e.monsterFeatures'), type: 'system', hasDerivedSubcategories: false },
- { id: 'multiattacks', name: game.i18n.localize('tokenActionHud.dnd5e.multiattacks'), type: 'system', hasDerivedSubcategories: false },
- { id: 'other-actions', name: game.i18n.localize('tokenActionHud.dnd5e.otherActions'), type: 'system', hasDerivedSubcategories: true },
- { id: 'pact-boons', name: game.i18n.localize('tokenActionHud.dnd5e.pactBoons'), type: 'system', hasDerivedSubcategories: false },
- { id: 'pact-spells', name: game.i18n.localize('tokenActionHud.dnd5e.pactSpells'), type: 'system', hasDerivedSubcategories: false },
- { id: 'passive-effects', name: game.i18n.localize('DND5E.EffectPassive'), type: 'system', hasDerivedSubcategories: false },
- { id: 'passive-features', name: game.i18n.localize('tokenActionHud.dnd5e.passiveFeatures'), type: 'system', hasDerivedSubcategories: false },
- { id: 'psionic-powers', name: game.i18n.localize('tokenActionHud.dnd5e.psionicPowers'), type: 'system', hasDerivedSubcategories: false },
- { id: 'race-features', name: game.i18n.localize('tokenActionHud.dnd5e.raceFeatures'), type: 'system', hasDerivedSubcategories: false },
- { id: 'reactions', name: game.i18n.localize('DND5E.ReactionPl'), type: 'system', hasDerivedSubcategories: true },
- { id: 'rests', name: game.i18n.localize('tokenActionHud.dnd5e.rests'), type: 'system', hasDerivedSubcategories: false },
- { id: 'runes', name: game.i18n.localize('tokenActionHud.dnd5e.runes'), type: 'system', hasDerivedSubcategories: false },
- { id: 'saves', name: game.i18n.localize('DND5E.ClassSaves'), type: 'system', hasDerivedSubcategories: false },
- { id: 'skills', name: game.i18n.localize('tokenActionHud.dnd5e.skills'), type: 'system', hasDerivedSubcategories: false },
- { id: 'superior-hunters-defense', name: game.i18n.localize('tokenActionHud.dnd5e.superiorHuntersDefense'), type: 'system', hasDerivedSubcategories: false },
- { id: 'temporary-effects', name: game.i18n.localize('DND5E.EffectTemporary'), type: 'system', hasDerivedSubcategories: false },
- { id: 'token', name: game.i18n.localize('tokenActionHud.token'), type: 'system', hasDerivedSubcategories: false },
- { id: 'tools', name: game.i18n.localize('ITEM.TypeToolPl'), type: 'system', hasDerivedSubcategories: false },
- { id: 'unequipped', name: game.i18n.localize('DND5E.Unequipped'), type: 'system', hasDerivedSubcategories: false },
- { id: 'utility', name: game.i18n.localize('tokenActionHud.utility'), type: 'system', hasDerivedSubcategories: false },
- { id: 'weapons', name: game.i18n.localize('ITEM.TypeWeaponPl'), type: 'system', hasDerivedSubcategories: false }
- ]
+ subcategories: subcategoriesArray
}
})
diff --git a/scripts/init.js b/scripts/init.js
index d605106..623bc74 100644
--- a/scripts/init.js
+++ b/scripts/init.js
@@ -1,7 +1,7 @@
import { SystemManager } from './system-manager.js'
import { MODULE, REQUIRED_CORE_MODULE_VERSION } from './constants.js'
-Hooks.once('ready', async () => {
+Hooks.on('tokenActionHudCoreApiReady', async () => {
const module = game.modules.get(MODULE.ID)
module.api = {
requiredCoreModuleVersion: REQUIRED_CORE_MODULE_VERSION,
diff --git a/scripts/magic-items-extender.js b/scripts/magic-items-extender.js
index 5666ea7..bc92f51 100644
--- a/scripts/magic-items-extender.js
+++ b/scripts/magic-items-extender.js
@@ -1,103 +1,102 @@
-import { CoreActionListExtender, CoreUtils } from './config.js'
-
-export class MagicItemActionListExtender extends CoreActionListExtender {
- constructor (actionHandler) {
- super(actionHandler.categoryManager)
- this.actionHandler = actionHandler
- this.categoryManager = actionHandler.categoryManager
- }
+export let MagicItemActionListExtender = null
+
+Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
+ MagicItemActionListExtender = class MagicItemActionListExtender extends coreModule.api.ActionListExtender {
+ constructor (actionHandler) {
+ super(actionHandler.categoryManager)
+ this.actionHandler = actionHandler
+ this.categoryManager = actionHandler.categoryManager
+ this.actor = null
+ }
- /**
+ /**
* Extend the action list
- * @param {object} character The actor and/or token
*/
- extendActionList (character) {
- const actorId = this.actionHandler.actorId
- const tokenId = this.actionHandler.tokenId
- if (!actorId) return
+ extendActionList () {
+ this.actor = this.actionHandler.actor
+ if (!this.actor) return
- const actor = MagicItems.actor(actorId)
+ const actor = MagicItems.actor(this.actor.id)
- if (!actor) return
+ if (!actor) return
- const magicItems = actor.items ?? []
+ const magicItems = actor.items ?? []
- if (magicItems.length === 0) return
+ if (magicItems.length === 0) return
- const parentSubcategoryId = 'magic-items'
- const parentSubcategoryType = 'system'
- const parentSubcategoryData = {
- id: parentSubcategoryId,
- type: parentSubcategoryType
- }
-
- magicItems.forEach((magicItem) => {
- if (magicItem.attuned && !this._isItemAttuned(magicItem)) return
- if (magicItem.equipped && !this._isItemEquipped(magicItem)) return
-
- const subcategoryId = `magic-items_${magicItem.id}`
- const subcategoryName = magicItem.name
- const subcategoryType = 'system-derived'
- const subcategoryInfo1 = `${magicItem.uses}/${magicItem.charges}`
- const subcategoryData = {
- id: subcategoryId,
- name: subcategoryName,
- type: subcategoryType,
- info1: subcategoryInfo1
+ const parentSubcategoryId = 'magic-items'
+ const parentSubcategoryType = 'system'
+ const parentSubcategoryData = {
+ id: parentSubcategoryId,
+ type: parentSubcategoryType
}
- // Add subcategory to action list
- this.actionHandler.addSubcategoryToActionList(parentSubcategoryData, subcategoryData)
-
- const actions = magicItem.ownedEntries.map((entry) => {
- const effect = entry.item
- const id = effect.id
- const name = effect.name
- const encodedValue = [
- 'magicItem',
- actorId,
- tokenId,
- `${magicItem.id}>${id}`
- ].join('|')
- const img = CoreUtils.getImage(effect)
- const info1 = effect.consumption
- const info2 = (effect.baseLevel) ? `${CoreUtils.i18n('DND5E.AbbreviationLevel')} ${effect.baseLevel}` : ''
- return {
- id,
- name,
- encodedValue,
- img,
- info1,
- info2,
- selected: true
+ magicItems.forEach((magicItem) => {
+ if (magicItem.attuned && !this._isItemAttuned(magicItem)) return
+ if (magicItem.equipped && !this._isItemEquipped(magicItem)) return
+
+ const subcategoryId = `magic-items_${magicItem.id}`
+ const subcategoryName = magicItem.name
+ const subcategoryType = 'system-derived'
+ const subcategoryInfo1 = `${magicItem.uses}/${magicItem.charges}`
+ const subcategoryData = {
+ id: subcategoryId,
+ name: subcategoryName,
+ type: subcategoryType,
+ info1: subcategoryInfo1
}
- })
- // Add actions to action list
- this.actionHandler.addActionsToActionList(actions, subcategoryData)
- })
- }
+ // Add subcategory to action list
+ this.actionHandler.addSubcategoryToActionList(parentSubcategoryData, subcategoryData)
+
+ const actions = magicItem.ownedEntries.map((entry) => {
+ const effect = entry.item
+ const id = effect.id
+ const name = effect.name
+ const encodedValue = [
+ 'magicItem',
+ `${magicItem.id}>${id}`
+ ].join('|')
+ const img = coreModule.api.Utils.getImage(effect)
+ const info1 = effect.consumption
+ const info2 = (effect.baseLevel) ? `${coreModule.api.Utils.i18n('DND5E.AbbreviationLevel')} ${effect.baseLevel}` : ''
+ return {
+ id,
+ name,
+ encodedValue,
+ img,
+ info1,
+ info2,
+ selected: true
+ }
+ })
+
+ // Add actions to action list
+ this.actionHandler.addActionsToActionList(actions, subcategoryData)
+ })
+ }
- /**
+ /**
* Whether the magic item is equipped or not
* @param {object} magicItem The item
* @returns {boolean}
*/
- _isItemEquipped (magicItem) {
- return magicItem.item.system.equipped
- }
+ _isItemEquipped (magicItem) {
+ return magicItem.item.system.equipped
+ }
- /**
+ /**
* Whether the magic items is attuned or not
* @param {object} magicItem The item
* @returns {boolean}
*/
- _isItemAttuned (magicItem) {
- const attunement = magicItem.item.system.attunment
- const attunementRequired = CONFIG.DND5E.attunementTypes?.REQUIRED ?? 1
+ _isItemAttuned (magicItem) {
+ const attunement = magicItem.item.system.attunment
+ const attunementRequired = CONFIG.DND5E.attunementTypes?.REQUIRED ?? 1
- if (attunement === attunementRequired) return false
+ if (attunement === attunementRequired) return false
- return true
+ return true
+ }
}
-}
+})
diff --git a/scripts/roll-handler-obsidian.js b/scripts/roll-handler-obsidian.js
index b72b690..317b2cb 100644
--- a/scripts/roll-handler-obsidian.js
+++ b/scripts/roll-handler-obsidian.js
@@ -5,13 +5,10 @@ export class RollHandlerObsidian extends RollHandler {
* Roll Ability Test
* @override
* @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
* @param {string} actionId
*/
- _rollAbilityTest (event, actorId, tokenId, actionId) {
- const actor = super.getActor(actorId, tokenId)
- OBSIDIAN.Items.roll(actor, { roll: 'abl', abl: actionId })
+ _rollAbilityTest (event, actionId) {
+ OBSIDIAN.Items.roll(super.actor, { roll: 'abl', abl: actionId })
}
/**
@@ -22,34 +19,27 @@ export class RollHandlerObsidian extends RollHandler {
* @param {string} tokenId
* @param {string} actionId
*/
- _rollAbilitySave (event, actorId, tokenId, actionId) {
- const actor = super.getActor(actorId, tokenId)
- OBSIDIAN.Items.roll(actor, { roll: 'save', save: actionId })
+ _rollAbilitySave (event, actionId) {
+ OBSIDIAN.Items.roll(super.actor, { roll: 'save', save: actionId })
}
/**
* Roll Skill
* @override
* @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
* @param {string} actionId
*/
- _rollSkill (event, actorId, tokenId, actionId) {
- const actor = super.getActor(actorId, tokenId)
- OBSIDIAN.Items.roll(actor, { roll: 'skl', skl: actionId })
+ _rollSkill (event, actionId) {
+ OBSIDIAN.Items.roll(super.actor, { roll: 'skl', skl: actionId })
}
/**
* Use Item
* @override
* @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
* @param {string} actionId
*/
- _useItem (event, actorId, tokenId, actionId) {
- const actor = super.getActor(actorId, tokenId)
- OBSIDIAN.Items.roll(actor, { roll: 'item', id: actionId })
+ _useItem (event, actionId) {
+ OBSIDIAN.Items.roll(super.actor, { roll: 'item', id: actionId })
}
}
diff --git a/scripts/roll-handler.js b/scripts/roll-handler.js
index 0fcbfe8..1f294bc 100644
--- a/scripts/roll-handler.js
+++ b/scripts/roll-handler.js
@@ -1,322 +1,289 @@
-// Core Module Imports
-import { CoreRollHandler, CoreUtils } from './config.js'
+export let RollHandler = null
-export class RollHandler extends CoreRollHandler {
+Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
+ RollHandler = class RollHandler extends coreModule.api.RollHandler {
/**
* Handle Action Event
* @override
* @param {object} event
* @param {string} encodedValue
*/
- async doHandleActionEvent (event, encodedValue) {
- const payload = encodedValue.split('|')
+ async doHandleActionEvent (event, encodedValue) {
+ const payload = encodedValue.split('|')
- if (payload.length !== 4) {
- super.throwInvalidValueErr()
- }
+ if (payload.length !== 2) {
+ super.throwInvalidValueErr()
+ }
- const actionType = payload[0]
- const actorId = payload[1]
- const tokenId = payload[2]
- const actionId = payload[3]
+ const actionType = payload[0]
+ const actionId = payload[1]
- if (actorId === 'multi' && tokenId === 'multi' && actionId !== 'toggleCombat') {
- for (const token of canvas.tokens.controlled) {
- const tokenActorId = token.actor?.id
- const tokenTokenId = token.id
- await this._handleMacros(
- event,
- actionType,
- tokenActorId,
- tokenTokenId,
- actionId
- )
+ if (!this.actor) {
+ for (const token of canvas.tokens.controlled) {
+ const actor = token.actor
+ await this._handleMacros(event, actionType, actor, token, actionId)
+ }
+ } else {
+ await this._handleMacros(event, actionType, this.actor, this.token, actionId)
}
- } else {
- await this._handleMacros(event, actionType, actorId, tokenId, actionId)
}
- }
- /**
- * Handle Macros
- * @private
- * @param {object} event
- * @param {string} actionType
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- async _handleMacros (event, actionType, actorId, tokenId, actionId) {
- switch (actionType) {
- case 'ability':
- this._rollAbility(event, actorId, tokenId, actionId)
- break
- case 'abilityCheck':
- this._rollAbilityTest(event, actorId, tokenId, actionId)
- break
- case 'abilitySave':
- this._rollAbilitySave(event, actorId, tokenId, actionId)
- break
- case 'condition':
- if (!tokenId) return
- await this._toggleCondition(event, tokenId, actionId)
- break
- case 'effect':
- await this._toggleEffect(event, actorId, tokenId, actionId)
- break
- case 'feature':
- case 'item':
- case 'spell':
- case 'weapon':
- if (this.isRenderItem()) this.doRenderItem(actorId, tokenId, actionId)
- else this._useItem(event, actorId, tokenId, actionId)
- break
- case 'magicItem':
- this._rollMagicItem(event, actorId, tokenId, actionId)
- break
- case 'skill':
- this._rollSkill(event, actorId, tokenId, actionId)
- break
- case 'utility':
- await this._performUtilityMacro(event, actorId, tokenId, actionId)
- break
- default:
- break
+ /**
+ * Handle Macros
+ * @private
+ * @param {object} event
+ * @param {string} actionType
+ * @param {object} actor
+ * @param {object} token
+ * @param {string} actionId
+ */
+ async _handleMacros (event, actionType, actor, token, actionId) {
+ switch (actionType) {
+ case 'ability':
+ this._rollAbility(event, actor, actionId)
+ break
+ case 'check':
+ this._rollAbilityTest(event, actor, actionId)
+ break
+ case 'save':
+ this._rollAbilitySave(event, actor, actionId)
+ break
+ case 'condition':
+ if (!token) return
+ await this._toggleCondition(event, token, actionId)
+ break
+ case 'effect':
+ await this._toggleEffect(event, actor, actionId)
+ break
+ case 'feature':
+ case 'item':
+ case 'spell':
+ case 'weapon':
+ if (this.isRenderItem()) this.doRenderItem(actor, actionId)
+ else this._useItem(event, actor, actionId)
+ break
+ case 'magicItem':
+ this._rollMagicItem(actor, actionId)
+ break
+ case 'skill':
+ this._rollSkill(event, actor, actionId)
+ break
+ case 'utility':
+ await this._performUtilityMacro(event, actor, token, actionId)
+ break
+ default:
+ break
+ }
}
- }
-
- /**
- * Roll Ability
- * @private
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- _rollAbility (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- actor.rollAbility(actionId, { event })
- }
-
- /**
- * Roll Ability Save
- * @private
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- _rollAbilitySave (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- actor.rollAbilitySave(actionId, { event })
- }
- /**
- * Roll Ability Test
- * @private
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- _rollAbilityTest (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- actor.rollAbilityTest(actionId, { event })
- }
+ /**
+ * Roll Ability
+ * @private
+ * @param {object} event The event
+ * @param {object} actor The actor
+ * @param {string} actionId The action id
+ */
+ _rollAbility (event, actor, actionId) {
+ if (!actor) return
+ actor.rollAbility(actionId, { event })
+ }
- /**
- * Roll Magic Item
- * @private
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- _rollMagicItem (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- const actionParts = actionId.split('>')
+ /**
+ * Roll Ability Save
+ * @private
+ * @param {object} event The event
+ * @param {object} actor The actor
+ * @param {string} actionId The action id
+ */
+ _rollAbilitySave (event, actor, actionId) {
+ if (!actor) return
+ actor.rollAbilitySave(actionId, { event })
+ }
- const itemId = actionParts[0]
- const magicEffectId = actionParts[1]
+ /**
+ * Roll Ability Test
+ * @private
+ * @param {object} event The event
+ * @param {object} actor The actor
+ * @param {string} actionId The action id
+ */
+ _rollAbilityTest (event, actor, actionId) {
+ if (!actor) return
+ actor.rollAbilityTest(actionId, { event })
+ }
- const magicItemActor = MagicItems.actor(actor.id)
+ /**
+ * Roll Magic Item
+ * @private
+ * @param {object} actor The actor
+ * @param {string} actionId The action id
+ */
+ _rollMagicItem (actor, actionId) {
+ const actionParts = actionId.split('>')
- // magicitems module 3.0.0 does not support Item5e#use
- magicItemActor.roll(itemId, magicEffectId)
+ const itemId = actionParts[0]
+ const magicEffectId = actionParts[1]
- Hooks.callAll('forceUpdateTokenActionHud')
- }
+ const magicItemActor = MagicItems.actor(actor.id)
- /**
- * Roll Skill
- * @private
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- _rollSkill (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- actor.rollSkill(actionId, { event })
- }
+ // magicitems module 3.0.0 does not support Item5e#use
+ magicItemActor.roll(itemId, magicEffectId)
- /**
- * Use Item
- * @private
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- * @returns {object}
- */
- _useItem (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- const item = CoreUtils.getItem(actor, actionId)
-
- if (this._needsRecharge(item)) {
- item.rollRecharge()
- return
+ Hooks.callAll('forceUpdateTokenActionHud')
}
- return item.use({ event })
- }
-
- /**
- * Needs Recharge
- * @private
- * @param {object} item
- * @returns {boolean}
- */
- _needsRecharge (item) {
- return (
- item.system.recharge &&
- !item.system.recharge.charged &&
- item.system.recharge.value
- )
- }
-
- /**
- * Perform Utility Macro
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- async _performUtilityMacro (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- const token = CoreUtils.getToken(tokenId)
+ /**
+ * Roll Skill
+ * @private
+ * @param {object} event The event
+ * @param {object} actor The actor
+ * @param {string} actionId The action id
+ */
+ _rollSkill (event, actor, actionId) {
+ if (!actor) return
+ actor.rollSkill(actionId, { event })
+ }
- switch (actionId) {
- case 'deathSave':
- actor.rollDeathSave({ event })
- break
- case 'endTurn':
- if (!token) break
- if (game.combat?.current?.tokenId === tokenId) {
- await game.combat?.nextTurn()
+ /**
+ * Use Item
+ * @private
+ * @param {object} event The event
+ * @param {object} actor The actor
+ * @param {string} actionId The action id
+ * @returns {object} The item
+ */
+ _useItem (event, actor, actionId) {
+ const item = coreModule.api.Utils.getItem(actor, actionId)
+
+ if (this._needsRecharge(item)) {
+ item.rollRecharge()
+ return
}
- break
- case 'initiative':
- await this._rollInitiative(actorId)
- break
- case 'inspiration': {
- const update = !actor.system.attributes.inspiration
- actor.update({ 'data.attributes.inspiration': update })
- break
+
+ return item.use({ event })
}
- case 'longRest':
- actor.longRest()
- break
- case 'shortRest':
- actor.shortRest()
- break
- case 'toggleCombat':
- if (canvas.tokens.controlled.length === 0) break
- await canvas.tokens.controlled[0].toggleCombat()
- break
- case 'toggleVisibility':
- if (!token) break
- token.toggleVisibility()
- break
+
+ /**
+ * Needs Recharge
+ * @private
+ * @param {object} item
+ * @returns {boolean}
+ */
+ _needsRecharge (item) {
+ return (
+ item.system.recharge &&
+ !item.system.recharge.charged &&
+ item.system.recharge.value
+ )
}
- // Update HUD
- Hooks.callAll('forceUpdateTokenActionHud')
- }
+ /**
+ * Perform Utility Macro
+ * @param {object} event The event
+ * @param {object} actor The actor
+ * @param {object} token The token
+ * @param {string} actionId The action id
+ */
+ async _performUtilityMacro (event, actor, token, actionId) {
+ switch (actionId) {
+ case 'deathSave':
+ actor.rollDeathSave({ event })
+ break
+ case 'endTurn':
+ if (!token) break
+ if (game.combat?.current?.tokenId === token.id) {
+ await game.combat?.nextTurn()
+ }
+ break
+ case 'initiative':
+ await this._rollInitiative(actor)
+ break
+ case 'inspiration': {
+ const update = !actor.system.attributes.inspiration
+ actor.update({ 'data.attributes.inspiration': update })
+ break
+ }
+ case 'longRest':
+ actor.longRest()
+ break
+ case 'shortRest':
+ actor.shortRest()
+ break
+ }
- /**
- * Roll Initiative
- * @private
- * @param {string} actorId
- * @param {string} tokenId
- */
- async _rollInitiative (actorId, tokenId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
+ // Update HUD
+ Hooks.callAll('forceUpdateTokenActionHud')
+ }
- await actor.rollInitiative({ createCombatants: true })
+ /**
+ * Roll Initiative
+ * @param {object} actor The actor
+ * @private
+ */
+ async _rollInitiative (actor) {
+ if (!actor) return
+ await actor.rollInitiative({ createCombatants: true })
- Hooks.callAll('forceUpdateTokenActionHud')
- }
+ Hooks.callAll('forceUpdateTokenActionHud')
+ }
- /**
- * Toggle Condition
- * @private
- * @param {object} event
- * @param {string} tokenId
- * @param {string} actionId
- * @param {object} effect
- */
- async _toggleCondition (event, tokenId, actionId, effect = null) {
- const token = CoreUtils.getToken(tokenId)
- if (!token) return
- const isRightClick = this.isRightClick(event)
- if (game.dfreds && effect?.flags?.isConvenient) {
- const effectLabel = effect.label
- game.dfreds.effectInterface._toggleEffect(effectLabel)
- } else {
- const condition = this.findCondition(actionId)
- if (!condition) return
+ /**
+ * Toggle Condition
+ * @private
+ * @param {object} event The event
+ * @param {object} token The token
+ * @param {string} actionId The action id
+ * @param {object} effect The effect
+ */
+ async _toggleCondition (event, token, actionId, effect = null) {
+ if (!token) return
+ const isRightClick = this.isRightClick(event)
+ if (game.dfreds && effect?.flags?.isConvenient) {
+ const effectLabel = effect.label
+ game.dfreds.effectInterface._toggleEffect(effectLabel)
+ } else {
+ const condition = this.findCondition(actionId)
+ if (!condition) return
+
+ isRightClick
+ ? await token.toggleEffect(condition, { overlay: true })
+ : await token.toggleEffect(condition)
+ }
- isRightClick
- ? await token.toggleEffect(condition, { overlay: true })
- : await token.toggleEffect(condition)
+ Hooks.callAll('forceUpdateTokenActionHud')
}
- Hooks.callAll('forceUpdateTokenActionHud')
- }
+ /**
+ * Get Condition
+ * @private
+ * @param {string} actionId
+ * @returns {object}
+ */
+ findCondition (actionId) {
+ return CONFIG.statusEffects.find((effect) => effect.id === actionId)
+ }
- /**
- * Get Condition
- * @private
- * @param {string} actionId
- * @returns {object}
- */
- findCondition (id) {
- return CONFIG.statusEffects.find((effect) => effect.id === id)
- }
+ /**
+ * Toggle Effect
+ * @param {object} event The event
+ * @param {object} actor The actor
+ * @param {string} actionId The action id
+ */
+ async _toggleEffect (event, actor, actionId) {
+ const effects = 'find' in actor.effects.entries ? actor.effects.entries : actor.effects
+ const effect = effects.find(effect => effect.id === actionId)
- /**
- * Toggle Effect
- * @param {object} event
- * @param {string} actorId
- * @param {string} tokenId
- * @param {string} actionId
- */
- async _toggleEffect (event, actorId, tokenId, actionId) {
- const actor = CoreUtils.getActor(actorId, tokenId)
- const effects = 'find' in actor.effects.entries ? actor.effects.entries : actor.effects
- const effect = effects.find((e) => e.id === actionId)
+ if (!effect) return
- if (!effect) return
+ const isRightClick = this.isRightClick(event)
- const isRightClick = this.isRightClick(event)
+ if (isRightClick) {
+ await effect.delete()
+ } else {
+ await effect.update({ disabled: !effect.disabled })
+ }
- if (isRightClick) {
- await effect.delete()
- } else {
- await effect.update({ disabled: !effect.disabled })
+ Hooks.callAll('forceUpdateTokenActionHud')
}
-
- Hooks.callAll('forceUpdateTokenActionHud')
}
-}
+})
diff --git a/scripts/system-manager.js b/scripts/system-manager.js
index cf0c8ab..5b2e0ca 100644
--- a/scripts/system-manager.js
+++ b/scripts/system-manager.js
@@ -3,66 +3,76 @@ import { ActionHandler } from './action-handler.js'
import { MagicItemActionListExtender } from './magic-items-extender.js'
import { RollHandler as Core } from './roll-handler.js'
import { RollHandlerObsidian as Obsidian5e } from './roll-handler-obsidian.js'
-import * as systemSettings from './settings.js'
import { DEFAULTS } from './defaults.js'
+import * as systemSettings from './settings.js'
-// Core Module Imports
-import { CoreSystemManager, CoreCategoryManager, CoreUtils } from './config.js'
-
-export class SystemManager extends CoreSystemManager {
- /** @override */
- doGetCategoryManager () {
- return new CoreCategoryManager()
- }
+export let SystemManager = null
+Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
+ SystemManager = class SystemManager extends coreModule.api.SystemManager {
/** @override */
- doGetActionHandler (categoryManager) {
- const actionHandler = new ActionHandler(categoryManager)
- if (CoreUtils.isModuleActive('magicitems')) { actionHandler.addFurtherActionHandler(new MagicItemActionListExtender(actionHandler)) }
- return actionHandler
- }
+ doGetCategoryManager () {
+ return new coreModule.api.CategoryManager()
+ }
- /** @override */
- getAvailableRollHandlers () {
- let coreTitle = 'Core D&D5e'
+ /** @override */
+ doGetActionHandler (categoryManager) {
+ const actionHandler = new ActionHandler(categoryManager)
+ if (coreModule.api.Utils.isModuleActive('magicitems')) { actionHandler.addFurtherActionHandler(new MagicItemActionListExtender(actionHandler)) }
+ return actionHandler
+ }
- if (CoreUtils.isModuleActive('midi-qol')) { coreTitle += ` [supports ${CoreUtils.getModuleTitle('midi-qol')}]` }
+ /** @override */
+ getAvailableRollHandlers () {
+ let coreTitle = 'Core D&D5e'
- const choices = { core: coreTitle }
- CoreSystemManager.addHandler(choices, 'obsidian')
+ if (coreModule.api.Utils.isModuleActive('midi-qol')) { coreTitle += ` [supports ${coreModule.api.Utils.getModuleTitle('midi-qol')}]` }
- return choices
- }
+ const choices = { core: coreTitle }
+ coreModule.api.SystemManager.addHandler(choices, 'obsidian')
- /** @override */
- doGetRollHandler (handlerId) {
- let rollHandler
- switch (handlerId) {
- case 'obsidian':
- rollHandler = new Obsidian5e()
- break
- case 'core':
- default:
- rollHandler = new Core()
- break
+ return choices
}
- return rollHandler
- }
+ /** @override */
+ doGetRollHandler (handlerId) {
+ let rollHandler
+ switch (handlerId) {
+ case 'obsidian':
+ rollHandler = new Obsidian5e()
+ break
+ case 'core':
+ default:
+ rollHandler = new Core()
+ break
+ }
- /** @override */
- doRegisterSettings (updateFunc) {
- systemSettings.register(updateFunc)
- }
+ return rollHandler
+ }
- /** @override */
- async doRegisterDefaultFlags () {
- const defaults = DEFAULTS
- // If the 'Magic Items' module is active, then add a subcategory for it
- if (game.modules.get('magicitems')?.active) {
- defaults.subcategories.push({ id: 'magic-items', name: CoreUtils.i18n('tokenActionHud.dnd5e.magicItems'), type: 'system', hasDerivedSubcategories: true })
- defaults.subcategories.sort((a, b) => a.id.localeCompare(b.id))
+ /** @override */
+ doRegisterSettings (updateFunc) {
+ systemSettings.register(updateFunc)
+ }
+
+ /** @override */
+ async doRegisterDefaultFlags () {
+ const defaults = DEFAULTS
+ // If the 'Magic Items' module is active, then add a subcategory for it
+ if (game.modules.get('magicitems')?.active) {
+ const name = coreModule.api.Utils.i18n('tokenActionHud.dnd5e.magicItems')
+ defaults.subcategories.push(
+ {
+ id: 'magic-items',
+ name,
+ listName: `Subcategory: ${name}`,
+ type: 'system',
+ hasDerivedSubcategories: true
+ }
+ )
+ defaults.subcategories.sort((a, b) => a.id.localeCompare(b.id))
+ }
+ return defaults
}
- return defaults
}
-}
+})
diff --git a/scripts/token-action-hud-dnd5e.min.js b/scripts/token-action-hud-dnd5e.min.js
index e6562a4..706e317 100644
--- a/scripts/token-action-hud-dnd5e.min.js
+++ b/scripts/token-action-hud-dnd5e.min.js
@@ -1 +1 @@
-const e=await import("../../token-action-hud-core/scripts/token-action-hud-core.min.js"),t=e.ActionHandler,s=e.ActionListExtender,i=e.CategoryManager,n=e.PreRollHandler,a=e.RollHandler,l=e.SystemManager,o=e.Utils,c=e.Logger,d={ID:"token-action-hud-dnd5e"},r={ID:"token-action-hud-core"},u="1.2",p={bonus:"fas fa-plus",crew:"fas fa-users",day:"fas fa-hourglass-end",hour:"fas fa-hourglass-half",lair:"fas fa-home",minute:"fas fa-hourglass-start",legendary:"fas fas fa-dragon",reaction:"fas fa-bolt",special:"fas fa-star"},m="fas fa-circle-c",g="fas fa-sun",h="fas fa-circle-r",y={.5:"fas fa-adjust",1:"fas fa-check",2:"fas fa-check-double"};let b=null;Hooks.on("i18nInit",(async()=>{b={categories:[{nestId:"inventory",id:"inventory",name:game.i18n.localize("DND5E.Inventory"),subcategories:[{nestId:"inventory_weapons",id:"weapons",name:game.i18n.localize("ITEM.TypeWeaponPl"),type:"system",hasDerivedSubcategories:!1},{nestId:"inventory_equipment",id:"equipment",name:game.i18n.localize("ITEM.TypeEquipmentPl"),type:"system",hasDerivedSubcategories:!1},{nestId:"inventory_consumables",id:"consumables",name:game.i18n.localize("ITEM.TypeConsumablePl"),type:"system",hasDerivedSubcategories:!1},{nestId:"inventory_tools",id:"tools",name:game.i18n.localize("ITEM.TypeToolPl"),type:"system",hasDerivedSubcategories:!1},{nestId:"inventory_containers",id:"containers",name:game.i18n.localize("ITEM.TypeContainerPl"),type:"system",hasDerivedSubcategories:!1},{nestId:"inventory_loot",id:"loot",name:game.i18n.localize("ITEM.TypeLootPl"),type:"system",hasDerivedSubcategories:!1}]},{nestId:"features",id:"features",name:game.i18n.localize("DND5E.Features"),subcategories:[{nestId:"features_active-features",id:"active-features",name:game.i18n.localize("tokenActionHud.dnd5e.activeFeatures"),type:"system",hasDerivedSubcategories:!1},{id:"passive-features",nestId:"features_passive-features",name:game.i18n.localize("tokenActionHud.dnd5e.passiveFeatures"),type:"system",hasDerivedSubcategories:!1}]},{nestId:"spells",id:"spells",name:game.i18n.localize("ITEM.TypeSpellPl"),subcategories:[{nestId:"spells_at-will-spells",id:"at-will-spells",name:game.i18n.localize("tokenActionHud.dnd5e.atWillSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_innate-spells",id:"innate-spells",name:game.i18n.localize("tokenActionHud.dnd5e.innateSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_pact-spells",id:"pact-spells",name:game.i18n.localize("tokenActionHud.dnd5e.pactSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_cantrips",id:"cantrips",name:game.i18n.localize("tokenActionHud.dnd5e.cantrips"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_1st-level-spells",id:"1st-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.1stLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_2nd-level-spells",id:"2nd-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.2ndLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_3rd-level-spells",id:"3rd-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.3rdLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_4th-level-spells",id:"4th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.4thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_5th-level-spells",id:"5th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.5thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_6th-level-spells",id:"6th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.6thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_7th-level-spells",id:"7th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.7thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_8th-level-spells",id:"8th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.8thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{nestId:"spells_9th-level-spells",id:"9th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.9thLevelSpells"),type:"system",hasDerivedSubcategories:!1}]},{nestId:"attributes",id:"attributes",name:game.i18n.localize("DND5E.Attributes"),subcategories:[{nestId:"attributes_abilities",id:"abilities",name:game.i18n.localize("tokenActionHud.dnd5e.abilities"),type:"system",hasDerivedSubcategories:!1},{nestId:"attributes_skills",id:"skills",name:game.i18n.localize("tokenActionHud.dnd5e.skills"),type:"system",hasDerivedSubcategories:!1}]},{nestId:"effects",id:"effects",name:game.i18n.localize("DND5E.Effects"),subcategories:[{nestId:"effects_temporary-effects",id:"temporary-effects",name:game.i18n.localize("DND5E.EffectTemporary"),type:"system",hasDerivedSubcategories:!1},{nestId:"effects_passive-effects",id:"passive-effects",name:game.i18n.localize("DND5E.EffectPassive"),type:"system",hasDerivedSubcategories:!1}]},{nestId:"conditions",id:"conditions",name:game.i18n.localize("tokenActionHud.dnd5e.conditions"),subcategories:[{nestId:"conditions_conditions",id:"conditions",name:game.i18n.localize("tokenActionHud.dnd5e.conditions"),type:"system",hasDerivedSubcategories:!1}]},{nestId:"utility",id:"utility",name:game.i18n.localize("tokenActionHud.utility"),subcategories:[{nestId:"utility_combat",id:"combat",name:game.i18n.localize("tokenActionHud.combat"),type:"system",hasDerivedSubcategories:!1},{nestId:"utility_token",id:"token",name:game.i18n.localize("tokenActionHud.token"),type:"system",hasDerivedSubcategories:!1},{nestId:"utility_rests",id:"rests",name:game.i18n.localize("tokenActionHud.dnd5e.rests"),type:"system",hasDerivedSubcategories:!1},{nestId:"utility_utility",id:"utility",name:game.i18n.localize("tokenActionHud.utility"),type:"system",hasDerivedSubcategories:!1}]}],subcategories:[{id:"1st-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.1stLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"2nd-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.2ndLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"3rd-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.3rdLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"4th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.4thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"5th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.5thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"6th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.6thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"7th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.7thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"8th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.8thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"9th-level-spells",name:game.i18n.localize("tokenActionHud.dnd5e.9thLevelSpells"),type:"system",hasDerivedSubcategories:!1},{id:"abilities",name:game.i18n.localize("tokenActionHud.dnd5e.abilities"),type:"system",hasDerivedSubcategories:!1},{id:"actions",name:game.i18n.localize("DND5E.ActionPl"),type:"system",hasDerivedSubcategories:!0},{id:"active-features",name:game.i18n.localize("tokenActionHud.dnd5e.activeFeatures"),type:"system",hasDerivedSubcategories:!1},{id:"artificer-infusions",name:game.i18n.localize("tokenActionHud.dnd5e.artificerInfusions"),type:"system",hasDerivedSubcategories:!1},{id:"at-will-spells",name:game.i18n.localize("tokenActionHud.dnd5e.atWillSpells"),type:"system",hasDerivedSubcategories:!1},{id:"background-features",name:game.i18n.localize("tokenActionHud.dnd5e.backgroundFeatures"),type:"system",hasDerivedSubcategories:!1},{id:"bonus-actions",name:game.i18n.localize("tokenActionHud.dnd5e.bonusActions"),type:"system",hasDerivedSubcategories:!0},{id:"cantrips",name:game.i18n.localize("tokenActionHud.dnd5e.cantrips"),type:"system",hasDerivedSubcategories:!1},{id:"channel-divinity",name:game.i18n.localize("tokenActionHud.dnd5e.channelDivinity"),type:"system",hasDerivedSubcategories:!1},{id:"checks",name:game.i18n.localize("tokenActionHud.dnd5e.checks"),type:"system",hasDerivedSubcategories:!1},{id:"class-features",name:game.i18n.localize("tokenActionHud.dnd5e.classFeatures"),type:"system",hasDerivedSubcategories:!1},{id:"combat",name:game.i18n.localize("tokenActionHud.combat"),type:"system",hasDerivedSubcategories:!1},{id:"conditions",name:game.i18n.localize("tokenActionHud.dnd5e.conditions"),type:"system",hasDerivedSubcategories:!1},{id:"consumables",name:game.i18n.localize("ITEM.TypeConsumablePl"),type:"system",hasDerivedSubcategories:!1},{id:"containers",name:game.i18n.localize("ITEM.TypeContainerPl"),type:"system",hasDerivedSubcategories:!1},{id:"crew-actions",name:game.i18n.localize("tokenActionHud.dnd5e.crewActions"),type:"system",hasDerivedSubcategories:!0},{id:"defensive-tactics",name:game.i18n.localize("tokenActionHud.dnd5e.defensiveTactics"),type:"system",hasDerivedSubcategories:!1},{id:"eldritch-invocations",name:game.i18n.localize("tokenActionHud.dnd5e.eldritchInvocations"),type:"system",hasDerivedSubcategories:!1},{id:"elemental-disciplines",name:game.i18n.localize("tokenActionHud.dnd5e.elementalDisciplines"),type:"system",hasDerivedSubcategories:!1},{id:"equipment",name:game.i18n.localize("ITEM.TypeEquipmentPl"),type:"system",hasDerivedSubcategories:!1},{id:"equipped",name:game.i18n.localize("DND5E.Equipped"),type:"system",hasDerivedSubcategories:!1},{id:"feats",name:game.i18n.localize("tokenActionHud.dnd5e.feats"),type:"system",hasDerivedSubcategories:!1},{id:"fighting-styles",name:game.i18n.localize("tokenActionHud.dnd5e.fightingStyles"),type:"system",hasDerivedSubcategories:!1},{id:"hunters-prey",name:game.i18n.localize("tokenActionHud.dnd5e.huntersPrey"),type:"system",hasDerivedSubcategories:!1},{id:"innate-spells",name:game.i18n.localize("tokenActionHud.dnd5e.innateSpells"),type:"system",hasDerivedSubcategories:!1},{id:"ki-abilities",name:game.i18n.localize("tokenActionHud.dnd5e.kiAbilities"),type:"system",hasDerivedSubcategories:!1},{id:"lair-actions",name:game.i18n.localize("tokenActionHud.dnd5e.lairActions"),type:"system",hasDerivedSubcategories:!0},{id:"legendary-actions",name:game.i18n.localize("tokenActionHud.dnd5e.legendaryActions"),type:"system",hasDerivedSubcategories:!0},{id:"loot",name:game.i18n.localize("ITEM.TypeLootPl"),type:"system",hasDerivedSubcategories:!1},{id:"maneuvers",name:game.i18n.localize("tokenActionHud.dnd5e.maneuvers"),type:"system",hasDerivedSubcategories:!1},{id:"metamagic-options",name:game.i18n.localize("tokenActionHud.dnd5e.metamagicOptions"),type:"system",hasDerivedSubcategories:!1},{id:"monster-features",name:game.i18n.localize("tokenActionHud.dnd5e.monsterFeatures"),type:"system",hasDerivedSubcategories:!1},{id:"multiattacks",name:game.i18n.localize("tokenActionHud.dnd5e.multiattacks"),type:"system",hasDerivedSubcategories:!1},{id:"other-actions",name:game.i18n.localize("tokenActionHud.dnd5e.otherActions"),type:"system",hasDerivedSubcategories:!0},{id:"pact-boons",name:game.i18n.localize("tokenActionHud.dnd5e.pactBoons"),type:"system",hasDerivedSubcategories:!1},{id:"pact-spells",name:game.i18n.localize("tokenActionHud.dnd5e.pactSpells"),type:"system",hasDerivedSubcategories:!1},{id:"passive-effects",name:game.i18n.localize("DND5E.EffectPassive"),type:"system",hasDerivedSubcategories:!1},{id:"passive-features",name:game.i18n.localize("tokenActionHud.dnd5e.passiveFeatures"),type:"system",hasDerivedSubcategories:!1},{id:"psionic-powers",name:game.i18n.localize("tokenActionHud.dnd5e.psionicPowers"),type:"system",hasDerivedSubcategories:!1},{id:"race-features",name:game.i18n.localize("tokenActionHud.dnd5e.raceFeatures"),type:"system",hasDerivedSubcategories:!1},{id:"reactions",name:game.i18n.localize("DND5E.ReactionPl"),type:"system",hasDerivedSubcategories:!0},{id:"rests",name:game.i18n.localize("tokenActionHud.dnd5e.rests"),type:"system",hasDerivedSubcategories:!1},{id:"runes",name:game.i18n.localize("tokenActionHud.dnd5e.runes"),type:"system",hasDerivedSubcategories:!1},{id:"saves",name:game.i18n.localize("DND5E.ClassSaves"),type:"system",hasDerivedSubcategories:!1},{id:"skills",name:game.i18n.localize("tokenActionHud.dnd5e.skills"),type:"system",hasDerivedSubcategories:!1},{id:"superior-hunters-defense",name:game.i18n.localize("tokenActionHud.dnd5e.superiorHuntersDefense"),type:"system",hasDerivedSubcategories:!1},{id:"temporary-effects",name:game.i18n.localize("DND5E.EffectTemporary"),type:"system",hasDerivedSubcategories:!1},{id:"token",name:game.i18n.localize("tokenActionHud.token"),type:"system",hasDerivedSubcategories:!1},{id:"tools",name:game.i18n.localize("ITEM.TypeToolPl"),type:"system",hasDerivedSubcategories:!1},{id:"unequipped",name:game.i18n.localize("DND5E.Unequipped"),type:"system",hasDerivedSubcategories:!1},{id:"utility",name:game.i18n.localize("tokenActionHud.utility"),type:"system",hasDerivedSubcategories:!1},{id:"weapons",name:game.i18n.localize("ITEM.TypeWeaponPl"),type:"system",hasDerivedSubcategories:!1}]}}));class MagicItemActionListExtender extends s{constructor(e){super(e.categoryManager),this.actionHandler=e,this.categoryManager=e.categoryManager}extendActionList(e){const t=this.actionHandler.actorId,s=this.actionHandler.tokenId;if(!t)return;const i=MagicItems.actor(t);if(!i)return;const n=i.items??[];if(0===n.length)return;const a={id:"magic-items",type:"system"};n.forEach((e=>{if(e.attuned&&!this._isItemAttuned(e))return;if(e.equipped&&!this._isItemEquipped(e))return;const i={id:`magic-items_${e.id}`,name:e.name,type:"system-derived",info1:`${e.uses}/${e.charges}`};this.actionHandler.addSubcategoryToActionList(a,i);const n=e.ownedEntries.map((i=>{const n=i.item,a=n.id;return{id:a,name:n.name,encodedValue:["magicItem",t,s,`${e.id}>${a}`].join("|"),img:o.getImage(n),info1:n.consumption,info2:n.baseLevel?`${o.i18n("DND5E.AbbreviationLevel")} ${n.baseLevel}`:"",selected:!0}}));this.actionHandler.addActionsToActionList(n,i)}))}_isItemEquipped(e){return e.item.system.equipped}_isItemAttuned(e){return e.item.system.attunment!==(CONFIG.DND5E.attunementTypes?.REQUIRED??1)}}class RollHandler extends a{async doHandleActionEvent(e,t){const s=t.split("|");4!==s.length&&super.throwInvalidValueErr();const i=s[0],n=s[1],a=s[2],l=s[3];if("multi"===n&&"multi"===a&&"toggleCombat"!==l)for(const t of canvas.tokens.controlled){const s=t.actor?.id,n=t.id;await this._handleMacros(e,i,s,n,l)}else await this._handleMacros(e,i,n,a,l)}async _handleMacros(e,t,s,i,n){switch(t){case"ability":this._rollAbility(e,s,i,n);break;case"abilityCheck":this._rollAbilityTest(e,s,i,n);break;case"abilitySave":this._rollAbilitySave(e,s,i,n);break;case"condition":if(!i)return;await this._toggleCondition(e,i,n);break;case"effect":await this._toggleEffect(e,s,i,n);break;case"feature":case"item":case"spell":case"weapon":this.isRenderItem()?this.doRenderItem(s,i,n):this._useItem(e,s,i,n);break;case"magicItem":this._rollMagicItem(e,s,i,n);break;case"skill":this._rollSkill(e,s,i,n);break;case"utility":await this._performUtilityMacro(e,s,i,n)}}_rollAbility(e,t,s,i){o.getActor(t,s).rollAbility(i,{event:e})}_rollAbilitySave(e,t,s,i){o.getActor(t,s).rollAbilitySave(i,{event:e})}_rollAbilityTest(e,t,s,i){o.getActor(t,s).rollAbilityTest(i,{event:e})}_rollMagicItem(e,t,s,i){const n=o.getActor(t,s),a=i.split(">"),l=a[0],c=a[1];MagicItems.actor(n.id).roll(l,c),Hooks.callAll("forceUpdateTokenActionHud")}_rollSkill(e,t,s,i){o.getActor(t,s).rollSkill(i,{event:e})}_useItem(e,t,s,i){const n=o.getActor(t,s),a=o.getItem(n,i);if(!this._needsRecharge(a))return a.use({event:e});a.rollRecharge()}_needsRecharge(e){return e.system.recharge&&!e.system.recharge.charged&&e.system.recharge.value}async _performUtilityMacro(e,t,s,i){const n=o.getActor(t,s),a=o.getToken(s);switch(i){case"deathSave":n.rollDeathSave({event:e});break;case"endTurn":if(!a)break;game.combat?.current?.tokenId===s&&await(game.combat?.nextTurn());break;case"initiative":await this._rollInitiative(t);break;case"inspiration":{const e=!n.system.attributes.inspiration;n.update({"data.attributes.inspiration":e});break}case"longRest":n.longRest();break;case"shortRest":n.shortRest();break;case"toggleCombat":if(0===canvas.tokens.controlled.length)break;await canvas.tokens.controlled[0].toggleCombat();break;case"toggleVisibility":if(!a)break;a.toggleVisibility()}Hooks.callAll("forceUpdateTokenActionHud")}async _rollInitiative(e,t){const s=o.getActor(e,t);await s.rollInitiative({createCombatants:!0}),Hooks.callAll("forceUpdateTokenActionHud")}async _toggleCondition(e,t,s,i=null){const n=o.getToken(t);if(!n)return;const a=this.isRightClick(e);if(game.dfreds&&i?.flags?.isConvenient){const e=i.label;game.dfreds.effectInterface._toggleEffect(e)}else{const e=this.findCondition(s);if(!e)return;a?await n.toggleEffect(e,{overlay:!0}):await n.toggleEffect(e)}Hooks.callAll("forceUpdateTokenActionHud")}findCondition(e){return CONFIG.statusEffects.find((t=>t.id===e))}async _toggleEffect(e,t,s,i){const n=o.getActor(t,s),a=("find"in n.effects.entries?n.effects.entries:n.effects).find((e=>e.id===i));if(!a)return;this.isRightClick(e)?await a.delete():await a.update({disabled:!a.disabled}),Hooks.callAll("forceUpdateTokenActionHud")}}class RollHandlerObsidian extends RollHandler{_rollAbilityTest(e,t,s,i){const n=super.getActor(t,s);OBSIDIAN.Items.roll(n,{roll:"abl",abl:i})}_rollAbilitySave(e,t,s,i){const n=super.getActor(t,s);OBSIDIAN.Items.roll(n,{roll:"save",save:i})}_rollSkill(e,t,s,i){const n=super.getActor(t,s);OBSIDIAN.Items.roll(n,{roll:"skl",skl:i})}_useItem(e,t,s,i){const n=super.getActor(t,s);OBSIDIAN.Items.roll(n,{roll:"item",id:i})}}class Utils{static getSetting(e,t=null){let s=t??null;try{s=game.settings.get(d.ID,e)}catch{c.debug(`Setting '${e}' not found`)}return s}static async setSetting(e,t){try{t=await game.settings.set(d.ID,e,t),c.debug(`Setting '${e}' set to '${t}'`)}catch{c.debug(`Setting '${e}' not found`)}}}class ActionHandler extends t{actor=null;actors=null;actorId=null;actorType=null;character=null;token=null;tokenId=null;items=null;abbreviateSkills=null;displaySpellInfo=null;showItemsWithoutActivationCosts=null;showUnchargedItems=null;showUnequippedItems=null;showUnpreparedSpells=null;subcategoryIds=null;activationSubcategoryIds=null;effectSubcategoryIds=null;featureSubcategoryIds=null;inventorySubcategoryIds=null;spellSubcategoryIds=null;featureActions=null;inventoryActions=null;spellActions=null;async buildSystemActions(e,t){if(this.actor=e?.actor,this.actorId=this.actor?.id??"multi",this.actors="multi"===this.actorId?this._getActors():[this.actor],this.actorType=this.actor?.type,this.token=e?.token,this.tokenId=this.token?.id??"multi","multi"!==this.actorId){let e=this.actor.items;e=this._discardSlowItems(e),e=this.sortItemsByName(e),this.items=e}this.abbreviateSkills=Utils.getSetting("abbreviateSkills"),this.displaySpellInfo=Utils.getSetting("displaySpellInfo"),this.showItemsWithoutActivationCosts=Utils.getSetting("showItemsWithoutActivationCosts"),this.showUnchargedItems=Utils.getSetting("showUnchargedItems"),this.showUnequippedItems=Utils.getSetting("showUnequippedItems"),this.showUnpreparedSpells=Utils.getSetting("showUnpreparedSpells"),this.subcategoryIds=t,this.activationSubcategoryIds=t.filter((e=>"actions"===e||"bonus-actions"===e||"crew-actions"===e||"lair-actions"===e||"legendary-actions"===e||"reactions"===e||"other-actions"===e)),this.effectSubcategoryIds=t.filter((e=>"passive-effects"===e||"temporary-effects"===e)),this.featureSubcategoryIds=t.filter((e=>"active-features"===e||"passive-features"===e||"background-features"===e||"class-features"===e||"feats"===e||"monster-features"===e||"race-features"===e||"artificer-infusions"===e||"channel-divinity"===e||"defensive-tactics"===e||"eldritch-invocations"===e||"elemental-disciplines"===e||"fighting-styles"===e||"hunters-prey"===e||"ki-abilities"===e||"maneuvers"===e||"metamagic-options"===e||"multiattacks"===e||"pact-boons"===e||"psionic-powers"===e||"runes"===e||"superior-hunters-defense"===e)),this.spellSubcategoryIds=t.filter((e=>"cantrips"===e||"1st-level-spells"===e||"2nd-level-spells"===e||"3rd-level-spells"===e||"4th-level-spells"===e||"5th-level-spells"===e||"6th-level-spells"===e||"7th-level-spells"===e||"8th-level-spells"===e||"9th-level-spells"===e||"at-will-spells"===e||"innate-spells"===e||"pact-spells"===e)),this.activationSubcategoryIds.length>0&&(this.featureSubcategoryIds=["active-features","passive-features"],this.spellSubcategoryIds=["cantrips","1st-level-spells","2nd-level-spells","3rd-level-spells","4th-level-spells","5th-level-spells","6th-level-spells","7th-level-spells","8th-level-spells","9th-level-spells","at-will-spells","innate-spells","pact-spells"]),"character"!==this.actorType&&"npc"!==this.actorType||(this.inventorySubcategoryIds=t.filter((e=>"equipped"===e||"consumables"===e||"containers"===e||"equipment"===e||"loot"===e||"tools"===e||"weapons"===e||"unequipped"===e)),this.activationSubcategoryIds.length>0&&(this.inventorySubcategoryIds=["consumables","containers","equipment","loot","tools","weapons"]),this._buildCharacterActions()),"vehicle"===this.actorType&&(this.inventorySubcategoryIds=this.subcategoryIds.filter((e=>"consumables"===e||"equipment"===e||"tools"===e||"weapons"===e)),this.activationSubcategoryIds.length>0&&(this.inventorySubcategoryIds=["consumables","equipment","tools","weapons"]),this._buildVehicleActions()),this.actor||this._buildMultipleTokenActions()}async _buildCharacterActions(){this._buildAbilities("ability","abilities"),this._buildAbilities("abilityCheck","checks"),this._buildAbilities("abilitySave","saves"),this._buildCombat(),this._buildConditions(),this._buildEffects(),this._buildFeatures(),this._buildInventory(),this._buildRests(),this._buildSkills(),this._buildSpells(),this._buildUtility()}async _buildVehicleActions(){this._buildAbilities("ability","abilities"),this._buildAbilities("abilityCheck","checks"),this._buildAbilities("abilitySave","saves"),this._buildCombat(),this._buildConditions(),this._buildEffects(),this._buildFeatures(),this._buildInventory(),this._buildUtility()}async _buildMultipleTokenActions(){this._buildAbilities("ability","abilities"),this._buildAbilities("abilityCheck","checks"),this._buildAbilities("abilitySave","saves"),this._buildCombat(),this._buildConditions(),this._buildRests(),this._buildSkills(),this._buildUtility()}_buildAbilities(e,t){if(!this.subcategoryIds.includes(t))return;const s="multi"===this.actorId?game.dnd5e.config.abilities:this.actor.system.abilities;if(0===s.length)return;const i=Object.entries(s).filter((e=>0!==s[e[0]].value)).map((i=>{const n=i[0],a=n.charAt(0).toUpperCase()+n.slice(1),l=this.abbreviateSkills?a:game.dnd5e.config.abilities[n],o=[e,this.actorId,this.tokenId,n].join(this.delimiter);return{id:n,name:l,encodedValue:o,icon:"checks"!==t?this._getProficiencyIcon(s[n].proficient):"",selected:!0}})),n={id:t,type:"system"};this.addActionsToActionList(i,n)}async buildActivations(e,t,s="item"){const i=new Map,n={actions:"action","bonus-actions":"bonus","crew-actions":"crew","lair-actions":"lair","legendary-actions":"legendary",reactions:"reaction","other-actions":"other"},a=["action","bonus","crew","lair","legendary","reaction"];for(const[t,s]of e){const e=s.system?.activation?.type,n=a.includes(e)?e:"other";i.has(n)||i.set(n,new Map),i.get(n).set(t,s)}for(const e of this.activationSubcategoryIds){const a=n[e];if(!i.has(a))continue;const l={...t,id:`${e}+${t.id}`,type:"system-derived"},o={id:e,type:"system"};await this.addSubcategoryToActionList(o,l),this.addSubcategoryInfo(t),this._buildActions(i.get(a),l,s)}}_buildCombat(){if(!this.subcategoryIds.includes("combat"))return;const e="utility",t={initiative:{id:"initiative",name:o.i18n("tokenActionHud.dnd5e.rollInitiative")},endTurn:{id:"endTurn",name:o.i18n("tokenActionHud.endTurn")}};game.combat?.current?.tokenId!==this.tokenId&&delete t.endTurn;const s=Object.entries(t).map((t=>{const s=t[1].id,i=t[1].name,n=[e,this.actorId,this.tokenId,s].join(this.delimiter),a={};let l="";if("initiative"===t[0]&&game.combat){const e=canvas.tokens.controlled.map((e=>e.id)),t=game.combat.combatants.filter((t=>e.includes(t.tokenId)));if(1===t.length){const e=t[0].initiative;a.class="tah-spotlight",a.text=e}l=`toggle${t.length>0&&t.every((e=>e?.initiative))?" active":""}`}return{id:s,name:i,encodedValue:n,info1:a,cssClass:l,selected:!0}}));this.addActionsToActionList(s,{id:"combat",type:"system"})}_buildConditions(){if(!this.subcategoryIds.includes("conditions"))return;if(!this.token)return;const e="condition",t=CONFIG.statusEffects.filter((e=>""!==e.id));if(0===t.length)return;const s=t.map((t=>{const s=t.id,i=o.i18n(t.label),n=[e,this.actorId,this.tokenId,s].join(this.delimiter),a=`toggle${this.actors.every((e=>e.effects.map((e=>e.flags?.core?.statusId)).some((e=>e===s))))?" active":""}`,l=o.getImage(t);return{id:s,name:i,encodedValue:n,img:l,cssClass:a,selected:!0}}));this.addActionsToActionList(s,{id:"conditions",type:"system"})}_buildEffects(){if(!this.effectSubcategoryIds)return;const e="effect",t=this.actor.effects;if(0===t.size)return;const s=new Map,i=new Map;for(const e of t){const t=e.id;e.isTemporary?i.set(t,e):s.set(t,e)}if(this.effectSubcategoryIds.includes("passive-effects")){const t={id:"passive-effects",type:"system"};this._buildActions(s,t,e)}if(this.effectSubcategoryIds.includes("temporary-effects")){const t={id:"temporary-effects",type:"system"};this._buildActions(i,t,e)}}_buildFeatures(){if(!this.featureSubcategoryIds)return;const e="feature",t=new Map;for(const[e,s]of this.items){"feat"===s.type&&t.set(e,s)}if(0===t.size)return;const s=new Map,i=[{type:"background",subcategoryId:"background-features"},{type:"class",subcategoryId:"class-features"},{type:"monster",subcategoryId:"monster-features"},{type:"race",subcategoryId:"race-features"},{type:"feats",subcategoryId:"feats"}],n=[{type:"artificerInfusion",subcategoryId:"artificer-infusions"},{type:"channelDivinity",subcategoryId:"channel-divinity"},{type:"defensiveTactic",subcategoryId:"defensive-tactics"},{type:"eldritchInvocation",subcategoryId:"eldritch-invocations"},{type:"elementalDiscipline",subcategoryId:"elemental-disciplines"},{type:"fightingStyle",subcategoryId:"fighting-styles"},{type:"huntersPrey",subcategoryId:"hunters-prey"},{type:"ki",subcategoryId:"ki-abilities"},{type:"maneuver",subcategoryId:"maneuvers"},{type:"metamagic",subcategoryId:"metamagic-options"},{type:"multiattack",subcategoryId:"multiattacks"},{type:"pact",subcategoryId:"pact-boons"},{type:"psionicPower",subcategoryId:"psionic-powers"},{type:"rune",subcategoryId:"runes"},{type:"superiorHuntersDefense",subcategoryId:"superior-hunters-defense"}];for(const[e,a]of t){const t=a.system.activation?.type,l=a.system.type.value,o=a.system.type?.subtype,c=["","lair","legendary"];t&&!c.includes(t)&&(s.has("active-features")||s.set("active-features",new Map),s.get("active-features").set(e,a)),t&&""!==t||(s.has("passive-features")||s.set("passive-features",new Map),s.get("passive-features").set(e,a));for(const t of i){const i=t.subcategoryId;t.type===l&&(s.has(i)||s.set(i,new Map),s.get(i).set(e,a))}for(const t of n){const i=t.subcategoryId;o&&t.type===o&&(s.has(i)||s.set(i,new Map),s.get(i).set(e,a))}}const a={"active-features":o.i18n("tokenActionHud.dnd5e.activeFeatures"),"passive-features":o.i18n("tokenActionHud.dnd5e.passiveFeatures")};for(const t of this.featureSubcategoryIds){if(!s.has(t))continue;const i={id:t,name:a[t]??"",type:"system"},n=s.get(t);this._buildActions(n,i,e),a[t]&&this.buildActivations(n,i,e)}}_buildInventory(){if(!this.inventorySubcategoryIds)return;if(0===this.items.size)return;const e=new Map;for(const[t,s]of this.items){const i=s.system.equipped,n=s.system?.quantity>0,a=this._isActiveItem(s),l=this._isUsableItem(s),o=this._isEquippedItem(s),c=s.type;n&&a&&(i&&(e.has("equipped")||e.set("equipped",new Map),e.get("equipped").set(t,s)),i||(e.has("unequipped")||e.set("unequipped",new Map),e.get("unequipped").set(t,s)),l&&"consumable"===c&&(e.has("consumables")||e.set("consumables",new Map),e.get("consumables").set(t,s)),o&&("backpack"===c&&(e.has("containers")||e.set("containers",new Map),e.get("containers").set(t,s)),"equipment"===c&&(e.has("equipment")||e.set("equipment",new Map),e.get("equipment").set(t,s)),"loot"===c&&(e.has("loot")||e.set("loot",new Map),e.get("loot").set(t,s)),"tool"===c&&(e.has("tools")||e.set("tools",new Map),e.get("tools").set(t,s)),"weapon"===c&&(e.has("weapons")||e.set("weapons",new Map),e.get("weapons").set(t,s))))}const t={equipped:o.i18n("DND5E.Equipped"),unequipped:o.i18n("DND5E.Unequipped"),consumables:o.i18n("ITEM.TypeConsumablePl"),containers:o.i18n("ITEM.TypeContainerPl"),equipment:o.i18n("ITEM.TypeEquipmentPl"),loot:o.i18n("ITEM.TypeLootPl"),tools:o.i18n("ITEM.TypeToolPl"),weapons:o.i18n("ITEM.TypeWeaponPl")};for(const s of this.inventorySubcategoryIds){if(!e.has(s))continue;const i={id:s,name:t[s],type:"system"},n=e.get(s);this._buildActions(n,i),this.activationSubcategoryIds&&this.buildActivations(n,i)}}_buildRests(){if(!this.subcategoryIds.includes("rests"))return;if(!this.actors.every((e=>"character"===e.type)))return;const e="utility",t={shortRest:{name:o.i18n("DND5E.ShortRest")},longRest:{name:o.i18n("DND5E.LongRest")}},s=Object.entries(t).map((t=>{const s=t[0],i=t[1].name,n=[e,this.actorId,this.tokenId,s].join(this.delimiter);return{id:s,name:i,encodedValue:n,selected:!0}}));this.addActionsToActionList(s,{id:"rests",type:"system"})}_buildSkills(){if(!this.subcategoryIds.includes("skills"))return;const e="skill",t="multi"===this.actorId?game.dnd5e.config.skills:this.actor.system.skills;if(0===t.length)return;const s=Object.entries(t).map((s=>{try{const i=s[0],n=i.charAt(0).toUpperCase()+i.slice(1),a=this.abbreviateSkills?n:game.dnd5e.config.skills[i].label,l=[e,this.actorId,this.tokenId,i].join(this.delimiter);return{id:i,name:a,encodedValue:l,icon:this._getProficiencyIcon(t[i].value)}}catch(e){return c.error(s),null}})).filter((e=>!!e));this.addActionsToActionList(s,{id:"skills",type:"system"})}_buildSpells(){if(!this.spellSubcategoryIds)return;const e="spell",t=new Map;for(const[e,s]of this.items){if("spell"===s.type){const i=this._isUsableItem(s),n=this._isUsableSpell(s);if(i&&n){switch(s.system.preparation.mode){case"atwill":t.has("at-will-spells")||t.set("at-will-spells",new Map),t.get("at-will-spells").set(e,s);break;case"innate":t.has("innate-spells")||t.set("innate-spells",new Map),t.get("innate-spells").set(e,s);break;case"pact":t.has("pact-spells")||t.set("pact-spells",new Map),t.get("pact-spells").set(e,s);break;default:switch(s.system.level){case 0:t.has("cantrips")||t.set("cantrips",new Map),t.get("cantrips").set(e,s);break;case 1:t.has("1st-level-spells")||t.set("1st-level-spells",new Map),t.get("1st-level-spells").set(e,s);break;case 2:t.has("2nd-level-spells")||t.set("2nd-level-spells",new Map),t.get("2nd-level-spells").set(e,s);break;case 3:t.has("3rd-level-spells")||t.set("3rd-level-spells",new Map),t.get("3rd-level-spells").set(e,s);break;case 4:t.has("4th-level-spells")||t.set("4th-level-spells",new Map),t.get("4th-level-spells").set(e,s);break;case 5:t.has("5th-level-spells")||t.set("5th-level-spells",new Map),t.get("5th-level-spells").set(e,s);break;case 6:t.has("6th-level-spells")||t.set("6th-level-spells",new Map),t.get("6th-level-spells").set(e,s);break;case 7:t.has("7th-level-spells")||t.set("7th-level-spells",new Map),t.get("7th-level-spells").set(e,s);break;case 8:t.has("8th-level-spells")||t.set("8th-level-spells",new Map),t.get("8th-level-spells").set(e,s);break;case 9:t.has("9th-level-spells")||t.set("9th-level-spells",new Map),t.get("9th-level-spells").set(e,s)}}}}}const s=Object.entries(this.actor.system.spells).reverse();let i=null;const n=[];let a=this.showUnchargedItems,l=this.showUnchargedItems;for(const[e,t]of s){const s=t.value>0,o=t.max>0,c=t.level>0;"pact"===e&&(!l&&s&&o&&c&&(l=!0),c||(l=!1),t.slotAvailable=l,i=[e,t]),e.startsWith("spell")&&"spell0"!==e?(!a&&s&&o&&(a=!0),t.slotAvailable=a,n.push([e,t])):s&&(t.slotsAvailable=!0,n.push(e,t))}if(i[1].slotAvailable){const e=n.findIndex((e=>e[0]==="spell"+i[1].level));n[e][1].slotsAvailable=!0}const c={"1st-level-spells":{spellMode:1,name:o.i18n("tokenActionHud.dnd5e.1stLevelSpells")},"2nd-level-spells":{spellMode:2,name:o.i18n("tokenActionHud.dnd5e.2ndLevelSpells")},"3rd-level-spells":{spellMode:3,name:o.i18n("tokenActionHud.dnd5e.3rdLevelSpells")},"4th-level-spells":{spellMode:4,name:o.i18n("tokenActionHud.dnd5e.4thLevelSpells")},"5th-level-spells":{spellMode:5,name:o.i18n("tokenActionHud.dnd5e.5thLevelSpells")},"6th-level-spells":{spellMode:6,name:o.i18n("tokenActionHud.dnd5e.6thLevelSpells")},"7th-level-spells":{spellMode:7,name:o.i18n("tokenActionHud.dnd5e.7thLevelSpells")},"8th-level-spells":{spellMode:8,name:o.i18n("tokenActionHud.dnd5e.8thLevelSpells")},"9th-level-spells":{spellMode:9,name:o.i18n("tokenActionHud.dnd5e.9thLevelSpells")},"at-will-spells":{spellMode:"atwill",name:o.i18n("tokenActionHud.dnd5e.atWillSpells")},cantrips:{spellMode:0,name:o.i18n("tokenActionHud.dnd5e.cantrips")},"innate-spells":{spellMode:"innate",name:o.i18n("tokenActionHud.dnd5e.innateSpells")},"pact-spells":{spellMode:"pact",name:o.i18n("tokenActionHud.dnd5e.pactSpells")}},d=["1","2","3","4","5","6","7","8","9","pact"];for(const s of this.spellSubcategoryIds){const a=c[s].spellMode,l=c[s].name;if(!t.has(s))continue;const o="pact"===a?i[1]:n.find((e=>e[0]===`spell${a}`))?.[1],r=o?.value,u=o?.max,p=o?.slotAvailable;if(!p&&d.includes(a))continue;const m={};m.info1={class:"tah-spotlight",text:u>=0?`${r}/${u}`:""};const g={id:s,name:l,type:"system",info:m};this.addSubcategoryInfo(g);const h=t.get(s);this._buildActions(h,g,e),this.activationSubcategoryIds&&this.buildActivations(h,g,e)}}_buildUtility(){if(!this.subcategoryIds.includes("utility"))return;if(!this.actors.every((e=>"character"===e.type)))return;const e="utility",t={deathSave:{name:o.i18n("DND5E.DeathSave")},inspiration:{name:o.i18n("DND5E.Inspiration")}};("multi"===this.actorId||this.actor.system.attributes.hp.value>0)&&delete t.deathSave;const s=Object.entries(t).map((t=>{const s=t[0],i=t[1].name,n=[e,this.actorId,this.tokenId,s].join(this.delimiter);let a="";if("inspiration"===t[0]){a=`toggle${this.actors.every((e=>e.system.attributes?.inspiration))?" active":""}`}return{id:s,name:i,encodedValue:n,cssClass:a,selected:!0}}));this.addActionsToActionList(s,{id:"utility",type:"system"})}_buildActions(e,t,s="item"){if(0===e.size)return;if(!("string"==typeof t?t:t?.id))return;const i=[...e].map((e=>this._getAction(s,e[1])));this.addActionsToActionList(i,t)}_getAction(e,t){const s=t.id??t._id;let i=t?.name??t?.label;t?.system?.recharge&&!t?.system?.recharge?.charged&&t?.system?.recharge?.value&&(i+=` (${o.i18n("DND5E.Recharge")})`);let n="";if(Object.hasOwn(t,"disabled")){n=`toggle${t.disabled?"":" active"}`}const a=[e,this.actorId,this.tokenId,s].join(this.delimiter),l=o.getImage(t),c=this._getActivationTypeIcon(t?.system?.activation?.type);let d=null,r=null;"spell"===t.type?(d=this._getPreparedIcon(t),this.displaySpellInfo&&(r=this._getSpellInfo(t))):r=this._getItemInfo(t);const u=r?.info1,p=r?.info2,m=r?.info3;return{id:s,name:i,encodedValue:a,cssClass:n,img:l,icon1:c,icon2:d,info1:u,info2:p,info3:m,selected:!0}}_isActiveItem(e){if(this.showItemsWithoutActivationCosts)return!0;const t=Object.keys(game.dnd5e.config.abilityActivationTypes).filter((e=>"none"!==e)),s=e.system.activation,i=s?.type;return!(!s||!t.includes(i))}_isEquippedItem(e){const t=e.type;if(this.showUnequippedItems&&!["consumable","spell","feat"].includes(t))return!0;return!(!e.system.equipped||"consumable"===t)}_isUsableItem(e){if(this.showUnchargedItems)return!0;return!!e.system.uses}_isUsableSpell(e){if("character"!==this.actorType&&this.showUnequippedItems)return!0;const t=e.system.preparation.prepared;if(this.showUnpreparedSpells)return!0;const s=e.system.level,i=Object.keys(game.dnd5e.config.spellPreparationModes).filter((e=>"prepared"!==e)),n=e.system.preparation.mode;return!(0!==s&&!i.includes(n)&&!t)}_getItemInfo(e){return{info1:{text:this._getQuantityData(e)},info2:{text:this._getUsesData(e)},info3:{text:this._getConsumeData(e)}}}_getSpellInfo(e){const t=e.system.components,s=[],i={},n={},a={};if(t?.vocal&&s.push(o.i18n("DND5E.ComponentVerbal")),t?.somatic&&s.push(o.i18n("DND5E.ComponentSomatic")),t?.material&&s.push(o.i18n("DND5E.ComponentMaterial")),s.length&&(i.title=s.join(", "),i.text=s.map((e=>e.charAt(0).toUpperCase())).join("")),t?.concentration){const e=o.i18n("DND5E.Concentration");n.title=e,n.text=e.charAt(0).toUpperCase()}if(t?.ritual){const e=o.i18n("DND5E.Ritual");a.title=e,a.text=e.charAt(0).toUpperCase()}return{info1:i,info2:n,info3:a}}_getActors(){const e=["character","npc"],t=canvas.tokens.controlled.map((e=>e.actor));if(t.every((t=>e.includes(t.type))))return t}_getQuantityData(e){const t=e?.system?.quantity??0;return t>1?t:""}_getUsesData(e){const t=e?.system?.uses;return t&&(t.value>0||t.max>0)?`${t.value??"0"}${t.max>0?`/${t.max}`:""}`:""}_getConsumeData(e){const t=e?.system?.consume?.target,s=e?.system?.consume?.type;if("attribute"===s){const e=t.substr(0,t.lastIndexOf(".")),s=this.actor.system[e];return s?`${s.value??"0"}${s.max?`/${s.max}`:""}`:""}const i=this.items.get(t);if("charges"===s){const e=i?.system.uses;return e?.value?`${e.value}${e.max?`/${e.max}`:""}`:""}return i?.system?.quantity??""}_discardSlowItems(e){if(Utils.getSetting("showSlowActions"))return e;const t=["minute","hour","day"],s=new Map;for(const[i,n]of e.entries()){const e=n.system.activation,a=n.system.activation?.type;e&&!t.includes(a)&&s.set(i,n)}return s}_getProficiencyIcon(e){const t=CONFIG.DND5E.proficiencyLevels[e]??"",s=y[e];if(s)return``}_getActivationTypeIcon(e){const t=CONFIG.DND5E.abilityActivationTypes[e]??"",s=p[e];if(s)return``}_getPreparedIcon(e){const t=e.system.level,s=e.system.preparation.mode,i=e.system.preparation.prepared,n=i?"fas fa-sun":"fas fa-sun tah-icon-disabled",a=i?o.i18n("DND5E.SpellPrepared"):o.i18n("DND5E.SpellUnprepared");return"prepared"===s&&0!==t?``:""}}function register(e){game.settings.register(d.ID,"abbreviateSkills",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.abbreviateSkills.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.abbreviateSkills.hint"),scope:"client",config:!0,type:Boolean,default:!1,onChange:t=>{e(t)}}),game.settings.register(d.ID,"showSlowActions",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showSlowActions.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showSlowActions.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:t=>{e(t)}}),game.settings.register(d.ID,"displaySpellInfo",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.displaySpellInfo.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.displaySpellInfo.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:t=>{e(t)}}),game.settings.register(d.ID,"showUnchargedItems",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnchargedItems.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnchargedItems.hint"),scope:"client",config:!0,type:Boolean,default:!1,onChange:t=>{e(t)}}),game.settings.register(d.ID,"showUnequippedItems",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnequippedItems.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnequippedItems.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:t=>{e(t)}}),game.settings.register(d.ID,"showUnpreparedSpells",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnpreparedSpells.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnpreparedSpells.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:t=>{e(t)}}),game.settings.register(d.ID,"showItemsWithoutActivationCosts",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showItemsWithoutActivationCosts.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showItemsWithoutActivationCosts.hint"),scope:"client",config:!0,type:Boolean,default:!1,onChange:t=>{e(t)}})}class SystemManager extends l{doGetCategoryManager(){return new i}doGetActionHandler(e){const t=new ActionHandler(e);return o.isModuleActive("magicitems")&&t.addFurtherActionHandler(new MagicItemActionListExtender(t)),t}getAvailableRollHandlers(){let e="Core D&D5e";o.isModuleActive("midi-qol")&&(e+=` [supports ${o.getModuleTitle("midi-qol")}]`);const t={core:e};return l.addHandler(t,"obsidian"),t}doGetRollHandler(e){let t;if("obsidian"===e)t=new RollHandlerObsidian;else t=new RollHandler;return t}doRegisterSettings(e){register(e)}async doRegisterDefaultFlags(){const e=b;return game.modules.get("magicitems")?.active&&(e.subcategories.push({id:"magic-items",name:o.i18n("tokenActionHud.dnd5e.magicItems"),type:"system",hasDerivedSubcategories:!0}),e.subcategories.sort(((e,t)=>e.id.localeCompare(t.id)))),e}}Hooks.once("ready",(async()=>{const e=game.modules.get(d.ID);e.api={requiredCoreModuleVersion:"1.2",SystemManager:SystemManager},Hooks.call("tokenActionHudSystemReady",e)}));export{p as ACTIVATION_TYPE_ICON,ActionHandler,m as CONCENTRATION_ICON,r as CORE_MODULE,t as CoreActionHandler,s as CoreActionListExtender,i as CoreCategoryManager,n as CorePreRollHandler,a as CoreRollHandler,l as CoreSystemManager,o as CoreUtils,b as DEFAULTS,c as Logger,d as MODULE,MagicItemActionListExtender,g as PREPARED_ICON,y as PROFICIENCY_LEVEL_ICON,u as REQUIRED_CORE_MODULE_VERSION,h as RITUAL_ICON,RollHandler,RollHandlerObsidian,SystemManager,Utils,register};
+const e={ID:"token-action-hud-dnd5e"},t={ID:"token-action-hud-core"},s="1.3",i={ability:"DND5E.Ability",check:"tokenActionHud.dnd5e.check",condition:"tokenActionHud.dnd5e.condition",effect:"DND5E.Effect",feature:"ITEM.TypeFeat",item:"tokenActionHud.dnd5e.item",save:"DND5E.ActionSave",skill:"tokenActionHud.dnd5e.skill",spell:"ITEM.TypeSpell",utility:"DND5E.ActionUtil"},n={bonus:"fas fa-plus",crew:"fas fa-users",day:"fas fa-hourglass-end",hour:"fas fa-hourglass-half",lair:"fas fa-home",minute:"fas fa-hourglass-start",legendary:"fas fas fa-dragon",reaction:"fas fa-bolt",special:"fas fa-star"},a="fas fa-circle-c",l="fas fa-sun",o="fas fa-circle-r",c={.5:"fas fa-adjust",1:"fas fa-check",2:"fas fa-check-double"},r={_1stLevelSpells:{id:"1st-level-spells",name:"tokenActionHud.dnd5e.1stLevelSpells",type:"system",hasDerivedSubcategories:!1},_2ndLevelSpells:{id:"2nd-level-spells",name:"tokenActionHud.dnd5e.2ndLevelSpells",type:"system",hasDerivedSubcategories:!1},_3rdLevelSpells:{id:"3rd-level-spells",name:"tokenActionHud.dnd5e.3rdLevelSpells",type:"system",hasDerivedSubcategories:!1},_4thLevelSpells:{id:"4th-level-spells",name:"tokenActionHud.dnd5e.4thLevelSpells",type:"system",hasDerivedSubcategories:!1},_5thLevelSpells:{id:"5th-level-spells",name:"tokenActionHud.dnd5e.5thLevelSpells",type:"system",hasDerivedSubcategories:!1},_6thLevelSpells:{id:"6th-level-spells",name:"tokenActionHud.dnd5e.6thLevelSpells",type:"system",hasDerivedSubcategories:!1},_7thLevelSpells:{id:"7th-level-spells",name:"tokenActionHud.dnd5e.7thLevelSpells",type:"system",hasDerivedSubcategories:!1},_8thLevelSpells:{id:"8th-level-spells",name:"tokenActionHud.dnd5e.8thLevelSpells",type:"system",hasDerivedSubcategories:!1},_9thLevelSpells:{id:"9th-level-spells",name:"tokenActionHud.dnd5e.9thLevelSpells",type:"system",hasDerivedSubcategories:!1},abilities:{id:"abilities",name:"tokenActionHud.dnd5e.abilities",type:"system",hasDerivedSubcategories:!1},actions:{id:"actions",name:"DND5E.ActionPl",type:"system",hasDerivedSubcategories:!0},activeFeatures:{id:"active-features",name:"tokenActionHud.dnd5e.activeFeatures",type:"system",hasDerivedSubcategories:!1},artificerInfusions:{id:"artificer-infusions",name:"tokenActionHud.dnd5e.artificerInfusions",type:"system",hasDerivedSubcategories:!1},atWillSpells:{id:"at-will-spells",name:"tokenActionHud.dnd5e.atWillSpells",type:"system",hasDerivedSubcategories:!1},backgroundFeatures:{id:"background-features",name:"tokenActionHud.dnd5e.backgroundFeatures",type:"system",hasDerivedSubcategories:!1},bonusActions:{id:"bonus-actions",name:"tokenActionHud.dnd5e.bonusActions",type:"system",hasDerivedSubcategories:!0},cantrips:{id:"cantrips",name:"tokenActionHud.dnd5e.cantrips",type:"system",hasDerivedSubcategories:!1},channelDivinity:{id:"channel-divinity",name:"tokenActionHud.dnd5e.channelDivinity",type:"system",hasDerivedSubcategories:!1},checks:{id:"checks",name:"tokenActionHud.dnd5e.checks",type:"system",hasDerivedSubcategories:!1},classFeatures:{id:"class-features",name:"tokenActionHud.dnd5e.classFeatures",type:"system",hasDerivedSubcategories:!1},combat:{id:"combat",name:"tokenActionHud.combat",type:"system",hasDerivedSubcategories:!1},conditions:{id:"conditions",name:"tokenActionHud.dnd5e.conditions",type:"system",hasDerivedSubcategories:!1},consumables:{id:"consumables",name:"ITEM.TypeConsumablePl",type:"system",hasDerivedSubcategories:!1},containers:{id:"containers",name:"ITEM.TypeContainerPl",type:"system",hasDerivedSubcategories:!1},crewActions:{id:"crew-actions",name:"tokenActionHud.dnd5e.crewActions",type:"system",hasDerivedSubcategories:!0},defensiveTactics:{id:"defensive-tactics",name:"tokenActionHud.dnd5e.defensiveTactics",type:"system",hasDerivedSubcategories:!1},eldritchInvocations:{id:"eldritch-invocations",name:"tokenActionHud.dnd5e.eldritchInvocations",type:"system",hasDerivedSubcategories:!1},elementalDisciplines:{id:"elemental-disciplines",name:"tokenActionHud.dnd5e.elementalDisciplines",type:"system",hasDerivedSubcategories:!1},equipment:{id:"equipment",name:"ITEM.TypeEquipmentPl",type:"system",hasDerivedSubcategories:!1},equipped:{id:"equipped",name:"DND5E.Equipped",type:"system",hasDerivedSubcategories:!1},feats:{id:"feats",name:"tokenActionHud.dnd5e.feats",type:"system",hasDerivedSubcategories:!1},fightingStyles:{id:"fighting-styles",name:"tokenActionHud.dnd5e.fightingStyles",type:"system",hasDerivedSubcategories:!1},huntersPrey:{id:"hunters-prey",name:"tokenActionHud.dnd5e.huntersPrey",type:"system",hasDerivedSubcategories:!1},innateSpells:{id:"innate-spells",name:"tokenActionHud.dnd5e.innateSpells",type:"system",hasDerivedSubcategories:!1},kiAbilities:{id:"ki-abilities",name:"tokenActionHud.dnd5e.kiAbilities",type:"system",hasDerivedSubcategories:!1},lairActions:{id:"lair-actions",name:"tokenActionHud.dnd5e.lairActions",type:"system",hasDerivedSubcategories:!0},legendaryActions:{id:"legendary-actions",name:"tokenActionHud.dnd5e.legendaryActions",type:"system",hasDerivedSubcategories:!0},loot:{id:"loot",name:"ITEM.TypeLootPl",type:"system",hasDerivedSubcategories:!1},maneuvers:{id:"maneuvers",name:"tokenActionHud.dnd5e.maneuvers",type:"system",hasDerivedSubcategories:!1},metamagicOptions:{id:"metamagic-options",name:"tokenActionHud.dnd5e.metamagicOptions",type:"system",hasDerivedSubcategories:!1},monsterFeatures:{id:"monster-features",name:"tokenActionHud.dnd5e.monsterFeatures",type:"system",hasDerivedSubcategories:!1},multiattacks:{id:"multiattacks",name:"tokenActionHud.dnd5e.multiattacks",type:"system",hasDerivedSubcategories:!1},otherActions:{id:"other-actions",name:"tokenActionHud.dnd5e.otherActions",type:"system",hasDerivedSubcategories:!0},pactBoons:{id:"pact-boons",name:"tokenActionHud.dnd5e.pactBoons",type:"system",hasDerivedSubcategories:!1},pactSpells:{id:"pact-spells",name:"tokenActionHud.dnd5e.pactSpells",type:"system",hasDerivedSubcategories:!1},passiveEffects:{id:"passive-effects",name:"DND5E.EffectPassive",type:"system",hasDerivedSubcategories:!1},passiveFeatures:{id:"passive-features",name:"tokenActionHud.dnd5e.passiveFeatures",type:"system",hasDerivedSubcategories:!1},psionicPowers:{id:"psionic-powers",name:"tokenActionHud.dnd5e.psionicPowers",type:"system",hasDerivedSubcategories:!1},raceFeatures:{id:"race-features",name:"tokenActionHud.dnd5e.raceFeatures",type:"system",hasDerivedSubcategories:!1},reactions:{id:"reactions",name:"DND5E.ReactionPl",type:"system",hasDerivedSubcategories:!0},rests:{id:"rests",name:"tokenActionHud.dnd5e.rests",type:"system",hasDerivedSubcategories:!1},runes:{id:"runes",name:"tokenActionHud.dnd5e.runes",type:"system",hasDerivedSubcategories:!1},saves:{id:"saves",name:"DND5E.ClassSaves",type:"system",hasDerivedSubcategories:!1},skills:{id:"skills",name:"tokenActionHud.dnd5e.skills",type:"system",hasDerivedSubcategories:!1},superiorHuntersDefense:{id:"superior-hunters-defense",name:"tokenActionHud.dnd5e.superiorHuntersDefense",type:"system",hasDerivedSubcategories:!1},temporaryEffects:{id:"temporary-effects",name:"DND5E.EffectTemporary",type:"system",hasDerivedSubcategories:!1},token:{id:"token",name:"tokenActionHud.token",type:"system",hasDerivedSubcategories:!1},tools:{id:"tools",name:"ITEM.TypeToolPl",type:"system",hasDerivedSubcategories:!1},unequipped:{id:"unequipped",name:"DND5E.Unequipped",type:"system",hasDerivedSubcategories:!1},utility:{id:"utility",name:"tokenActionHud.utility",type:"system",hasDerivedSubcategories:!1},weapons:{id:"weapons",name:"ITEM.TypeWeaponPl",type:"system",hasDerivedSubcategories:!1}};let d=null;Hooks.once("tokenActionHudCoreApiReady",(async t=>{d=class Utils{static getSetting(s,i=null){let n=i??null;try{n=game.settings.get(e.ID,s)}catch{t.api.Logger.debug(`Setting '${s}' not found`)}return n}static async setSetting(s,i){try{i=await game.settings.set(e.ID,s,i),t.api.Logger.debug(`Setting '${s}' set to '${i}'`)}catch{t.api.Logger.debug(`Setting '${s}' not found`)}}}}));let p=null;Hooks.once("tokenActionHudCoreApiReady",(async e=>{p=class ActionHandler extends e.api.ActionHandler{actors=null;tokens=null;actorType=null;items=null;abbreviateSkills=null;displaySpellInfo=null;showItemsWithoutActivationCosts=null;showUnchargedItems=null;showUnequippedItems=null;showUnpreparedSpells=null;activationSubcategoryIds=null;featureSubcategoryIds=null;inventorySubcategoryIds=null;spellSubcategoryIds=null;featureActions=null;inventoryActions=null;spellActions=null;async buildSystemActions(e){if(this.actors=this.actor?[this.actor]:this._getActors(),this.tokens=this.token?[this.token]:this._getTokens(),this.actorType=this.actor?.type,this.actor){let e=this.actor.items;e=this._discardSlowItems(e),e=this.sortItemsByName(e),this.items=e}this.abbreviateSkills=d.getSetting("abbreviateSkills"),this.displaySpellInfo=d.getSetting("displaySpellInfo"),this.showItemsWithoutActivationCosts=d.getSetting("showItemsWithoutActivationCosts"),this.showUnchargedItems=d.getSetting("showUnchargedItems"),this.showUnequippedItems=d.getSetting("showUnequippedItems"),this.showUnpreparedSpells=d.getSetting("showUnpreparedSpells"),this.activationSubcategoryIds=["actions","bonus-actions","crew-actions","lair-actions","legendary-actions","reactions","other-actions"],this.featureSubcategoryIds=["active-features","passive-features","background-features","class-features","feats","monster-features","race-features","artificer-infusions","channel-divinity","defensive-tactics","eldritch-invocations","elemental-disciplines","fighting-styles","hunters-prey","ki-abilities","maneuvers","metamagic-options","multiattacks","pact-boons","psionic-powers","runes","superior-hunters-defense"],this.spellSubcategoryIds=["cantrips","1st-level-spells","2nd-level-spells","3rd-level-spells","4th-level-spells","5th-level-spells","6th-level-spells","7th-level-spells","8th-level-spells","9th-level-spells","at-will-spells","innate-spells","pact-spells"],"character"!==this.actorType&&"npc"!==this.actorType||(this.inventorySubcategoryIds=["equipped","consumables","containers","equipment","loot","tools","weapons","unequipped"],this._buildCharacterActions()),"vehicle"===this.actorType&&(this.inventorySubcategoryIds=["consumables","equipment","tools","weapons"],this._buildVehicleActions()),this.actor||this._buildMultipleTokenActions()}async _buildCharacterActions(){this._buildAbilities("ability","abilities"),this._buildAbilities("check","checks"),this._buildAbilities("save","saves"),this._buildCombat(),this._buildConditions(),this._buildEffects(),this._buildFeatures(),this._buildInventory(),this._buildRests(),this._buildSkills(),this._buildSpells(),this._buildUtility()}async _buildVehicleActions(){this._buildAbilities("ability","abilities"),this._buildAbilities("check","checks"),this._buildAbilities("save","saves"),this._buildCombat(),this._buildConditions(),this._buildEffects(),this._buildFeatures(),this._buildInventory(),this._buildUtility()}async _buildMultipleTokenActions(){this._buildAbilities("ability","abilities"),this._buildAbilities("check","checks"),this._buildAbilities("save","saves"),this._buildCombat(),this._buildConditions(),this._buildRests(),this._buildSkills(),this._buildUtility()}_buildAbilities(t,s){const n=this.actor?this.actor.system.abilities:game.dnd5e.config.abilities;if(0===n.length)return;const a=Object.entries(n).filter((e=>0!==n[e[0]].value)).map((a=>{const l=a[0],o=`${t}-${a[0]}`,c=l.charAt(0).toUpperCase()+l.slice(1),r=this.abbreviateSkills?c:game.dnd5e.config.abilities[l],d=`${`${e.api.Utils.i18n(i[t])}: `??""}${game.dnd5e.config.abilities[l]}`;return{id:o,name:r,encodedValue:[t,l].join(this.delimiter),icon:"checks"!==s?this._getProficiencyIcon(n[l].proficient):"",listName:d}})),l={id:s,type:"system"};this.addActionsToActionList(a,l)}async buildActivations(e,t,s="item"){const i=new Map,n={actions:"action","bonus-actions":"bonus","crew-actions":"crew","lair-actions":"lair","legendary-actions":"legendary",reactions:"reaction","other-actions":"other"},a=["action","bonus","crew","lair","legendary","reaction"];for(const[t,s]of e){const e=s.system?.activation?.type,n=a.includes(e)?e:"other";i.has(n)||i.set(n,new Map),i.get(n).set(t,s)}for(const e of this.activationSubcategoryIds){const a=n[e];if(!i.has(a))continue;const l={...t,id:`${e}+${t.id}`,type:"system-derived"},o={id:e,type:"system"};await this.addSubcategoryToActionList(o,l),this.addSubcategoryInfo(t),this._buildActions(i.get(a),l,s)}}_buildCombat(){const t="utility",s={initiative:{id:"initiative",name:e.api.Utils.i18n("tokenActionHud.dnd5e.rollInitiative")},endTurn:{id:"endTurn",name:e.api.Utils.i18n("tokenActionHud.endTurn")}};game.combat?.current?.tokenId!==this.token?.id&&delete s.endTurn;const n=Object.entries(s).map((s=>{const n=s[1].id,a=s[1].name,l=`${`${e.api.Utils.i18n(i[t])}: `??""}${a}`,o=[t,n].join(this.delimiter),c={};let r="";if("initiative"===s[0]&&game.combat){const e=canvas.tokens.controlled.map((e=>e.id)),t=game.combat.combatants.filter((t=>e.includes(t.tokenId)));if(1===t.length){const e=t[0].initiative;c.class="tah-spotlight",c.text=e}r=`toggle${t.length>0&&t.every((e=>e?.initiative))?" active":""}`}return{id:n,name:a,encodedValue:o,info1:c,cssClass:r,listName:l}}));this.addActionsToActionList(n,{id:"combat",type:"system"})}_buildConditions(){if(!this.token&&0===this.tokens.length)return;const t="condition",s=CONFIG.statusEffects.filter((e=>""!==e.id));if(0===s.length)return;const n=s.map((s=>{const n=s.id,a=e.api.Utils.i18n(s.label),l=`${`${e.api.Utils.i18n(i[t])}: `??""}${a}`,o=[t,n].join(this.delimiter),c=`toggle${this.actors.every((e=>e.effects.map((e=>e.flags?.core?.statusId)).some((e=>e===n))))?" active":""}`,r=e.api.Utils.getImage(s);return{id:n,name:a,encodedValue:o,img:r,cssClass:c,listName:l}}));this.addActionsToActionList(n,{id:"conditions",type:"system"})}_buildEffects(){const e="effect",t=this.actor.effects;if(0===t.size)return;const s=new Map,i=new Map;for(const e of t){const t=e.id;e.isTemporary?i.set(t,e):s.set(t,e)}this._buildActions(s,{id:"passive-effects",type:"system"},e),this._buildActions(i,{id:"temporary-effects",type:"system"},e)}_buildFeatures(){const t="feature",s=new Map;for(const[e,t]of this.items){"feat"===t.type&&s.set(e,t)}if(0===s.size)return;const i=new Map,n=[{type:"background",subcategoryId:"background-features"},{type:"class",subcategoryId:"class-features"},{type:"monster",subcategoryId:"monster-features"},{type:"race",subcategoryId:"race-features"},{type:"feats",subcategoryId:"feats"}],a=[{type:"artificerInfusion",subcategoryId:"artificer-infusions"},{type:"channelDivinity",subcategoryId:"channel-divinity"},{type:"defensiveTactic",subcategoryId:"defensive-tactics"},{type:"eldritchInvocation",subcategoryId:"eldritch-invocations"},{type:"elementalDiscipline",subcategoryId:"elemental-disciplines"},{type:"fightingStyle",subcategoryId:"fighting-styles"},{type:"huntersPrey",subcategoryId:"hunters-prey"},{type:"ki",subcategoryId:"ki-abilities"},{type:"maneuver",subcategoryId:"maneuvers"},{type:"metamagic",subcategoryId:"metamagic-options"},{type:"multiattack",subcategoryId:"multiattacks"},{type:"pact",subcategoryId:"pact-boons"},{type:"psionicPower",subcategoryId:"psionic-powers"},{type:"rune",subcategoryId:"runes"},{type:"superiorHuntersDefense",subcategoryId:"superior-hunters-defense"}];for(const[e,t]of s){const s=t.system.activation?.type,l=t.system.type.value,o=t.system.type?.subtype,c=["","lair","legendary"];s&&!c.includes(s)&&(i.has("active-features")||i.set("active-features",new Map),i.get("active-features").set(e,t)),s&&""!==s||(i.has("passive-features")||i.set("passive-features",new Map),i.get("passive-features").set(e,t));for(const s of n){const n=s.subcategoryId;s.type===l&&(i.has(n)||i.set(n,new Map),i.get(n).set(e,t))}for(const s of a){const n=s.subcategoryId;o&&s.type===o&&(i.has(n)||i.set(n,new Map),i.get(n).set(e,t))}}const l={"active-features":e.api.Utils.i18n("tokenActionHud.dnd5e.activeFeatures"),"passive-features":e.api.Utils.i18n("tokenActionHud.dnd5e.passiveFeatures")};for(const e of this.featureSubcategoryIds){if(!i.has(e))continue;const s={id:e,name:l[e]??"",type:"system"},n=i.get(e);this._buildActions(n,s,t),l[e]&&this.buildActivations(n,s,t)}}_buildInventory(){if(0===this.items.size)return;const t=new Map;for(const[e,s]of this.items){const i=s.system.equipped,n=s.system?.quantity>0,a=this._isActiveItem(s),l=this._isUsableItem(s),o=this._isEquippedItem(s),c=s.type;n&&a&&(i&&(t.has("equipped")||t.set("equipped",new Map),t.get("equipped").set(e,s)),i||(t.has("unequipped")||t.set("unequipped",new Map),t.get("unequipped").set(e,s)),l&&"consumable"===c&&(t.has("consumables")||t.set("consumables",new Map),t.get("consumables").set(e,s)),o&&("backpack"===c&&(t.has("containers")||t.set("containers",new Map),t.get("containers").set(e,s)),"equipment"===c&&(t.has("equipment")||t.set("equipment",new Map),t.get("equipment").set(e,s)),"loot"===c&&(t.has("loot")||t.set("loot",new Map),t.get("loot").set(e,s)),"tool"===c&&(t.has("tools")||t.set("tools",new Map),t.get("tools").set(e,s)),"weapon"===c&&(t.has("weapons")||t.set("weapons",new Map),t.get("weapons").set(e,s))))}const s={equipped:e.api.Utils.i18n("DND5E.Equipped"),unequipped:e.api.Utils.i18n("DND5E.Unequipped"),consumables:e.api.Utils.i18n("ITEM.TypeConsumablePl"),containers:e.api.Utils.i18n("ITEM.TypeContainerPl"),equipment:e.api.Utils.i18n("ITEM.TypeEquipmentPl"),loot:e.api.Utils.i18n("ITEM.TypeLootPl"),tools:e.api.Utils.i18n("ITEM.TypeToolPl"),weapons:e.api.Utils.i18n("ITEM.TypeWeaponPl")};for(const e of this.inventorySubcategoryIds){if(!t.has(e))continue;const i={id:e,name:s[e],type:"system"},n=t.get(e);this._buildActions(n,i),this.activationSubcategoryIds&&this.buildActivations(n,i)}}_buildRests(){if(!this.actors.every((e=>"character"===e.type)))return;const t="utility",s={shortRest:{name:e.api.Utils.i18n("DND5E.ShortRest")},longRest:{name:e.api.Utils.i18n("DND5E.LongRest")}},n=Object.entries(s).map((s=>{const n=s[0],a=s[1].name,l=`${`${e.api.Utils.i18n(i[t])}: `??""}${a}`,o=[t,n].join(this.delimiter);return{id:n,name:a,encodedValue:o,listName:l}}));this.addActionsToActionList(n,{id:"rests",type:"system"})}_buildSkills(){const t="skill",s=this.actor?this.actor.system.skills:game.dnd5e.config.skills;if(0===s.length)return;const n=Object.entries(s).map((n=>{try{const a=n[0],l=a.charAt(0).toUpperCase()+a.slice(1),o=this.abbreviateSkills?l:game.dnd5e.config.skills[a].label,c=`${`${e.api.Utils.i18n(i[t])}: `??""}${game.dnd5e.config.skills[a].label}`,r=[t,a].join(this.delimiter);return{id:a,name:o,encodedValue:r,icon:this._getProficiencyIcon(s[a].value),listName:c}}catch(t){return e.api.Logger.error(n),null}})).filter((e=>!!e));this.addActionsToActionList(n,{id:"skills",type:"system"})}_buildSpells(){const t="spell",s=new Map;for(const[e,t]of this.items){if("spell"===t.type){const i=this._isUsableItem(t),n=this._isUsableSpell(t);if(i&&n){switch(t.system.preparation.mode){case"atwill":s.has("at-will-spells")||s.set("at-will-spells",new Map),s.get("at-will-spells").set(e,t);break;case"innate":s.has("innate-spells")||s.set("innate-spells",new Map),s.get("innate-spells").set(e,t);break;case"pact":s.has("pact-spells")||s.set("pact-spells",new Map),s.get("pact-spells").set(e,t);break;default:switch(t.system.level){case 0:s.has("cantrips")||s.set("cantrips",new Map),s.get("cantrips").set(e,t);break;case 1:s.has("1st-level-spells")||s.set("1st-level-spells",new Map),s.get("1st-level-spells").set(e,t);break;case 2:s.has("2nd-level-spells")||s.set("2nd-level-spells",new Map),s.get("2nd-level-spells").set(e,t);break;case 3:s.has("3rd-level-spells")||s.set("3rd-level-spells",new Map),s.get("3rd-level-spells").set(e,t);break;case 4:s.has("4th-level-spells")||s.set("4th-level-spells",new Map),s.get("4th-level-spells").set(e,t);break;case 5:s.has("5th-level-spells")||s.set("5th-level-spells",new Map),s.get("5th-level-spells").set(e,t);break;case 6:s.has("6th-level-spells")||s.set("6th-level-spells",new Map),s.get("6th-level-spells").set(e,t);break;case 7:s.has("7th-level-spells")||s.set("7th-level-spells",new Map),s.get("7th-level-spells").set(e,t);break;case 8:s.has("8th-level-spells")||s.set("8th-level-spells",new Map),s.get("8th-level-spells").set(e,t);break;case 9:s.has("9th-level-spells")||s.set("9th-level-spells",new Map),s.get("9th-level-spells").set(e,t)}}}}}const i=Object.entries(this.actor.system.spells).reverse();let n=null;const a=[];let l=this.showUnchargedItems,o=this.showUnchargedItems;for(const[e,t]of i){const s=t.value>0,i=t.max>0,c=t.level>0;"pact"===e&&(!o&&s&&i&&c&&(o=!0),c||(o=!1),t.slotAvailable=o,n=[e,t]),e.startsWith("spell")&&"spell0"!==e?(!l&&s&&i&&(l=!0),t.slotAvailable=l,a.push([e,t])):s&&(t.slotsAvailable=!0,a.push(e,t))}if(n[1].slotAvailable){const e=a.findIndex((e=>e[0]==="spell"+n[1].level));a[e][1].slotsAvailable=!0}const c={"1st-level-spells":{spellMode:1,name:e.api.Utils.i18n("tokenActionHud.dnd5e.1stLevelSpells")},"2nd-level-spells":{spellMode:2,name:e.api.Utils.i18n("tokenActionHud.dnd5e.2ndLevelSpells")},"3rd-level-spells":{spellMode:3,name:e.api.Utils.i18n("tokenActionHud.dnd5e.3rdLevelSpells")},"4th-level-spells":{spellMode:4,name:e.api.Utils.i18n("tokenActionHud.dnd5e.4thLevelSpells")},"5th-level-spells":{spellMode:5,name:e.api.Utils.i18n("tokenActionHud.dnd5e.5thLevelSpells")},"6th-level-spells":{spellMode:6,name:e.api.Utils.i18n("tokenActionHud.dnd5e.6thLevelSpells")},"7th-level-spells":{spellMode:7,name:e.api.Utils.i18n("tokenActionHud.dnd5e.7thLevelSpells")},"8th-level-spells":{spellMode:8,name:e.api.Utils.i18n("tokenActionHud.dnd5e.8thLevelSpells")},"9th-level-spells":{spellMode:9,name:e.api.Utils.i18n("tokenActionHud.dnd5e.9thLevelSpells")},"at-will-spells":{spellMode:"atwill",name:e.api.Utils.i18n("tokenActionHud.dnd5e.atWillSpells")},cantrips:{spellMode:0,name:e.api.Utils.i18n("tokenActionHud.dnd5e.cantrips")},"innate-spells":{spellMode:"innate",name:e.api.Utils.i18n("tokenActionHud.dnd5e.innateSpells")},"pact-spells":{spellMode:"pact",name:e.api.Utils.i18n("tokenActionHud.dnd5e.pactSpells")}},r=["1","2","3","4","5","6","7","8","9","pact"];for(const e of this.spellSubcategoryIds){const i=c[e].spellMode,l=c[e].name;if(!s.has(e))continue;const o="pact"===i?n[1]:a.find((e=>e[0]===`spell${i}`))?.[1],d=o?.value,p=o?.max,u=o?.slotAvailable;if(!u&&r.includes(i))continue;const h={};h.info1={class:"tah-spotlight",text:p>=0?`${d}/${p}`:""};const m={id:e,name:l,type:"system",info:h};this.addSubcategoryInfo(m);const y=s.get(e);this._buildActions(y,m,t),this.activationSubcategoryIds&&this.buildActivations(y,m,t)}}_buildUtility(){if(!this.actors.every((e=>"character"===e.type)))return;const t="utility",s={deathSave:{name:e.api.Utils.i18n("DND5E.DeathSave")},inspiration:{name:e.api.Utils.i18n("DND5E.Inspiration")}};(!this.actor||this.actor.system.attributes.hp.value>0)&&delete s.deathSave;const n=Object.entries(s).map((s=>{const n=s[0],a=s[1].name,l=`${`${e.api.Utils.i18n(i[t])}: `??""}${a}`,o=[t,n].join(this.delimiter);let c="";if("inspiration"===s[0]){c=`toggle${this.actors.every((e=>e.system.attributes?.inspiration))?" active":""}`}return{id:n,name:a,encodedValue:o,cssClass:c,listName:l}}));this.addActionsToActionList(n,{id:"utility",type:"system"})}_buildActions(e,t,s="item"){if(0===e.size)return;if(!("string"==typeof t?t:t?.id))return;const i=[...e].map((e=>this._getAction(s,e[1])));this.addActionsToActionList(i,t)}_getAction(t,s){const n=s.id??s._id;let a=s?.name??s?.label;s?.system?.recharge&&!s?.system?.recharge?.charged&&s?.system?.recharge?.value&&(a+=` (${e.api.Utils.i18n("DND5E.Recharge")})`);const l=`${`${e.api.Utils.i18n(i[t])}: `??""}${a}`;let o="";if(Object.hasOwn(s,"disabled")){o=`toggle${s.disabled?"":" active"}`}const c=[t,n].join(this.delimiter),r=e.api.Utils.getImage(s),d=this._getActivationTypeIcon(s?.system?.activation?.type);let p=null,u=null;"spell"===s.type?(p=this._getPreparedIcon(s),this.displaySpellInfo&&(u=this._getSpellInfo(s))):u=this._getItemInfo(s);const h=u?.info1,m=u?.info2,y=u?.info3;return{id:n,name:a,encodedValue:c,cssClass:o,img:r,icon1:d,icon2:p,info1:h,info2:m,info3:y,listName:l}}_isActiveItem(e){if(this.showItemsWithoutActivationCosts)return!0;const t=Object.keys(game.dnd5e.config.abilityActivationTypes).filter((e=>"none"!==e)),s=e.system.activation,i=s?.type;return!(!s||!t.includes(i))}_isEquippedItem(e){const t=e.type;if(this.showUnequippedItems&&!["consumable","spell","feat"].includes(t))return!0;return!(!e.system.equipped||"consumable"===t)}_isUsableItem(e){if(this.showUnchargedItems)return!0;return!!e.system.uses}_isUsableSpell(e){if("character"!==this.actorType&&this.showUnequippedItems)return!0;const t=e.system.preparation.prepared;if(this.showUnpreparedSpells)return!0;const s=e.system.level,i=Object.keys(game.dnd5e.config.spellPreparationModes).filter((e=>"prepared"!==e)),n=e.system.preparation.mode;return!(0!==s&&!i.includes(n)&&!t)}_getItemInfo(e){return{info1:{text:this._getQuantityData(e)},info2:{text:this._getUsesData(e)},info3:{text:this._getConsumeData(e)}}}_getSpellInfo(t){const s=t.system.components,i=[],n={},a={},l={};if(s?.vocal&&i.push(e.api.Utils.i18n("DND5E.ComponentVerbal")),s?.somatic&&i.push(e.api.Utils.i18n("DND5E.ComponentSomatic")),s?.material&&i.push(e.api.Utils.i18n("DND5E.ComponentMaterial")),i.length&&(n.title=i.join(", "),n.text=i.map((e=>e.charAt(0).toUpperCase())).join("")),s?.concentration){const t=e.api.Utils.i18n("DND5E.Concentration");a.title=t,a.text=t.charAt(0).toUpperCase()}if(s?.ritual){const t=e.api.Utils.i18n("DND5E.Ritual");l.title=t,l.text=t.charAt(0).toUpperCase()}return{info1:n,info2:a,info3:l}}_getActors(){const e=["character","npc"],t=canvas.tokens.controlled.filter((e=>e.actor)).map((e=>e.actor));if(t.every((t=>e.includes(t.type))))return t}_getTokens(){const e=["character","npc"],t=canvas.tokens.controlled;if(t.filter((e=>e.actor)).map((e=>e.actor)).every((t=>e.includes(t.type))))return t}_getQuantityData(e){const t=e?.system?.quantity??0;return t>1?t:""}_getUsesData(e){const t=e?.system?.uses;return t&&(t.value>0||t.max>0)?`${t.value??"0"}${t.max>0?`/${t.max}`:""}`:""}_getConsumeData(e){const t=e?.system?.consume?.target,s=e?.system?.consume?.type;if("attribute"===s){const e=t.substr(0,t.lastIndexOf(".")),s=this.actor.system[e];return s?`${s.value??"0"}${s.max?`/${s.max}`:""}`:""}const i=this.items.get(t);if("charges"===s){const e=i?.system.uses;return e?.value?`${e.value}${e.max?`/${e.max}`:""}`:""}return i?.system?.quantity??""}_discardSlowItems(e){if(d.getSetting("showSlowActions"))return e;const t=["minute","hour","day"],s=new Map;for(const[i,n]of e.entries()){const e=n.system.activation,a=n.system.activation?.type;e&&!t.includes(a)&&s.set(i,n)}return s}_getProficiencyIcon(e){const t=CONFIG.DND5E.proficiencyLevels[e]??"",s=c[e];if(s)return``}_getActivationTypeIcon(e){const t=CONFIG.DND5E.abilityActivationTypes[e]??"",s=n[e];if(s)return``}_getPreparedIcon(t){const s=t.system.level,i=t.system.preparation.mode,n=t.system.preparation.prepared,a=n?"fas fa-sun":"fas fa-sun tah-icon-disabled",l=n?e.api.Utils.i18n("DND5E.SpellPrepared"):e.api.Utils.i18n("DND5E.SpellUnprepared");return"prepared"===i&&0!==s?``:""}}}));let u=null;Hooks.once("tokenActionHudCoreApiReady",(async e=>{u=class MagicItemActionListExtender extends e.api.ActionListExtender{constructor(e){super(e.categoryManager),this.actionHandler=e,this.categoryManager=e.categoryManager,this.actor=null}extendActionList(){if(this.actor=this.actionHandler.actor,!this.actor)return;const t=MagicItems.actor(this.actor.id);if(!t)return;const s=t.items??[];if(0===s.length)return;const i={id:"magic-items",type:"system"};s.forEach((t=>{if(t.attuned&&!this._isItemAttuned(t))return;if(t.equipped&&!this._isItemEquipped(t))return;const s={id:`magic-items_${t.id}`,name:t.name,type:"system-derived",info1:`${t.uses}/${t.charges}`};this.actionHandler.addSubcategoryToActionList(i,s);const n=t.ownedEntries.map((s=>{const i=s.item,n=i.id;return{id:n,name:i.name,encodedValue:["magicItem",`${t.id}>${n}`].join("|"),img:e.api.Utils.getImage(i),info1:i.consumption,info2:i.baseLevel?`${e.api.Utils.i18n("DND5E.AbbreviationLevel")} ${i.baseLevel}`:"",selected:!0}}));this.actionHandler.addActionsToActionList(n,s)}))}_isItemEquipped(e){return e.item.system.equipped}_isItemAttuned(e){return e.item.system.attunment!==(CONFIG.DND5E.attunementTypes?.REQUIRED??1)}}}));let h=null;Hooks.once("tokenActionHudCoreApiReady",(async e=>{const t=r;Object.values(t).forEach((t=>{t.name=e.api.Utils.i18n(t.name),t.listName=`Subcategory: ${e.api.Utils.i18n(t.name)}`}));const s=Object.values(t);h={categories:[{nestId:"inventory",id:"inventory",name:e.api.Utils.i18n("DND5E.Inventory"),subcategories:[{...t.weapons,nestId:"inventory_weapons"},{...t.equipment,nestId:"inventory_equipment"},{...t.consumables,nestId:"inventory_consumables"},{...t.tools,nestId:"inventory_tools"},{...t.containers,nestId:"inventory_containers"},{...t.loot,nestId:"inventory_loot"}]},{nestId:"features",id:"features",name:e.api.Utils.i18n("DND5E.Features"),subcategories:[{...t.activeFeatures,nestId:"features_active-features"},{...t.passiveFeatures,nestId:"features_passive-features"}]},{nestId:"spells",id:"spells",name:e.api.Utils.i18n("ITEM.TypeSpellPl"),subcategories:[{...t.atWillSpells,nestId:"spells_at-will-spells"},{...t.innateSpells,nestId:"spells_innate-spells"},{...t.pactSpells,nestId:"spells_pact-spells"},{...t.cantrips,nestId:"spells_cantrips"},{...t._1stLevelSpells,nestId:"spells_1st-level-spells"},{...t._2ndLevelSpells,nestId:"spells_2nd-level-spells"},{...t._3rdLevelSpells,nestId:"spells_3rd-level-spells"},{...t._4thLevelSpells,nestId:"spells_4th-level-spells"},{...t._5thLevelSpells,nestId:"spells_5th-level-spells"},{...t._6thLevelSpells,nestId:"spells_6th-level-spells"},{...t._7thLevelSpells,nestId:"spells_7th-level-spells"},{...t._8thLevelSpells,nestId:"spells_8th-level-spells"},{...t._9thLevelSpells,nestId:"spells_9th-level-spells"}]},{nestId:"attributes",id:"attributes",name:e.api.Utils.i18n("DND5E.Attributes"),subcategories:[{...t.abilities,nestId:"attributes_abilities"},{...t.skills,nestId:"attributes_skills"}]},{nestId:"effects",id:"effects",name:e.api.Utils.i18n("DND5E.Effects"),subcategories:[{...t.temporaryEffects,nestId:"effects_temporary-effects"},{...t.passiveEffects,nestId:"effects_passive-effects"}]},{nestId:"conditions",id:"conditions",name:e.api.Utils.i18n("tokenActionHud.dnd5e.conditions"),subcategories:[{...t.conditions,nestId:"conditions_conditions"}]},{nestId:"utility",id:"utility",name:e.api.Utils.i18n("tokenActionHud.utility"),subcategories:[{...t.combat,nestId:"utility_combat"},{...t.token,nestId:"utility_token"},{...t.rests,nestId:"utility_rests"},{...t.utility,nestId:"utility_utility"}]}],subcategories:s}}));let m=null;function register(t){game.settings.register(e.ID,"abbreviateSkills",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.abbreviateSkills.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.abbreviateSkills.hint"),scope:"client",config:!0,type:Boolean,default:!1,onChange:e=>{t(e)}}),game.settings.register(e.ID,"showSlowActions",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showSlowActions.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showSlowActions.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:e=>{t(e)}}),game.settings.register(e.ID,"displaySpellInfo",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.displaySpellInfo.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.displaySpellInfo.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:e=>{t(e)}}),game.settings.register(e.ID,"showUnchargedItems",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnchargedItems.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnchargedItems.hint"),scope:"client",config:!0,type:Boolean,default:!1,onChange:e=>{t(e)}}),game.settings.register(e.ID,"showUnequippedItems",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnequippedItems.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnequippedItems.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:e=>{t(e)}}),game.settings.register(e.ID,"showUnpreparedSpells",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnpreparedSpells.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showUnpreparedSpells.hint"),scope:"client",config:!0,type:Boolean,default:!0,onChange:e=>{t(e)}}),game.settings.register(e.ID,"showItemsWithoutActivationCosts",{name:game.i18n.localize("tokenActionHud.dnd5e.settings.showItemsWithoutActivationCosts.name"),hint:game.i18n.localize("tokenActionHud.dnd5e.settings.showItemsWithoutActivationCosts.hint"),scope:"client",config:!0,type:Boolean,default:!1,onChange:e=>{t(e)}})}Hooks.once("tokenActionHudCoreApiReady",(async e=>{m=class RollHandler extends e.api.RollHandler{async doHandleActionEvent(e,t){const s=t.split("|");2!==s.length&&super.throwInvalidValueErr();const i=s[0],n=s[1];if(this.actor)await this._handleMacros(e,i,this.actor,this.token,n);else for(const t of canvas.tokens.controlled){const s=t.actor;await this._handleMacros(e,i,s,t,n)}}async _handleMacros(e,t,s,i,n){switch(t){case"ability":this._rollAbility(e,s,n);break;case"check":this._rollAbilityTest(e,s,n);break;case"save":this._rollAbilitySave(e,s,n);break;case"condition":if(!i)return;await this._toggleCondition(e,i,n);break;case"effect":await this._toggleEffect(e,s,n);break;case"feature":case"item":case"spell":case"weapon":this.isRenderItem()?this.doRenderItem(s,n):this._useItem(e,s,n);break;case"magicItem":this._rollMagicItem(s,n);break;case"skill":this._rollSkill(e,s,n);break;case"utility":await this._performUtilityMacro(e,s,i,n)}}_rollAbility(e,t,s){t&&t.rollAbility(s,{event:e})}_rollAbilitySave(e,t,s){t&&t.rollAbilitySave(s,{event:e})}_rollAbilityTest(e,t,s){t&&t.rollAbilityTest(s,{event:e})}_rollMagicItem(e,t){const s=t.split(">"),i=s[0],n=s[1];MagicItems.actor(e.id).roll(i,n),Hooks.callAll("forceUpdateTokenActionHud")}_rollSkill(e,t,s){t&&t.rollSkill(s,{event:e})}_useItem(t,s,i){const n=e.api.Utils.getItem(s,i);if(!this._needsRecharge(n))return n.use({event:t});n.rollRecharge()}_needsRecharge(e){return e.system.recharge&&!e.system.recharge.charged&&e.system.recharge.value}async _performUtilityMacro(e,t,s,i){switch(i){case"deathSave":t.rollDeathSave({event:e});break;case"endTurn":if(!s)break;game.combat?.current?.tokenId===s.id&&await(game.combat?.nextTurn());break;case"initiative":await this._rollInitiative(t);break;case"inspiration":{const e=!t.system.attributes.inspiration;t.update({"data.attributes.inspiration":e});break}case"longRest":t.longRest();break;case"shortRest":t.shortRest()}Hooks.callAll("forceUpdateTokenActionHud")}async _rollInitiative(e){e&&(await e.rollInitiative({createCombatants:!0}),Hooks.callAll("forceUpdateTokenActionHud"))}async _toggleCondition(e,t,s,i=null){if(!t)return;const n=this.isRightClick(e);if(game.dfreds&&i?.flags?.isConvenient){const e=i.label;game.dfreds.effectInterface._toggleEffect(e)}else{const e=this.findCondition(s);if(!e)return;n?await t.toggleEffect(e,{overlay:!0}):await t.toggleEffect(e)}Hooks.callAll("forceUpdateTokenActionHud")}findCondition(e){return CONFIG.statusEffects.find((t=>t.id===e))}async _toggleEffect(e,t,s){const i=("find"in t.effects.entries?t.effects.entries:t.effects).find((e=>e.id===s));if(!i)return;this.isRightClick(e)?await i.delete():await i.update({disabled:!i.disabled}),Hooks.callAll("forceUpdateTokenActionHud")}}}));class RollHandlerObsidian extends m{_rollAbilityTest(e,t){OBSIDIAN.Items.roll(super.actor,{roll:"abl",abl:t})}_rollAbilitySave(e,t){OBSIDIAN.Items.roll(super.actor,{roll:"save",save:t})}_rollSkill(e,t){OBSIDIAN.Items.roll(super.actor,{roll:"skl",skl:t})}_useItem(e,t){OBSIDIAN.Items.roll(super.actor,{roll:"item",id:t})}}let y=null;Hooks.once("tokenActionHudCoreApiReady",(async e=>{y=class SystemManager extends e.api.SystemManager{doGetCategoryManager(){return new e.api.CategoryManager}doGetActionHandler(t){const s=new p(t);return e.api.Utils.isModuleActive("magicitems")&&s.addFurtherActionHandler(new u(s)),s}getAvailableRollHandlers(){let t="Core D&D5e";e.api.Utils.isModuleActive("midi-qol")&&(t+=` [supports ${e.api.Utils.getModuleTitle("midi-qol")}]`);const s={core:t};return e.api.SystemManager.addHandler(s,"obsidian"),s}doGetRollHandler(e){let t;if("obsidian"===e)t=new RollHandlerObsidian;else t=new m;return t}doRegisterSettings(e){register(e)}async doRegisterDefaultFlags(){const t=h;if(game.modules.get("magicitems")?.active){const s=e.api.Utils.i18n("tokenActionHud.dnd5e.magicItems");t.subcategories.push({id:"magic-items",name:s,listName:`Subcategory: ${s}`,type:"system",hasDerivedSubcategories:!0}),t.subcategories.sort(((e,t)=>e.id.localeCompare(t.id)))}return t}}})),Hooks.on("tokenActionHudCoreApiReady",(async()=>{const t=game.modules.get(e.ID);t.api={requiredCoreModuleVersion:"1.3",SystemManager:y},Hooks.call("tokenActionHudSystemReady",t)}));export{i as ACTION_TYPE,n as ACTIVATION_TYPE_ICON,p as ActionHandler,a as CONCENTRATION_ICON,t as CORE_MODULE,h as DEFAULTS,e as MODULE,u as MagicItemActionListExtender,l as PREPARED_ICON,c as PROFICIENCY_LEVEL_ICON,s as REQUIRED_CORE_MODULE_VERSION,o as RITUAL_ICON,m as RollHandler,RollHandlerObsidian,r as SUBCATEGORY,y as SystemManager,d as Utils,register};
diff --git a/scripts/utils.js b/scripts/utils.js
index 70da86b..07c1c32 100644
--- a/scripts/utils.js
+++ b/scripts/utils.js
@@ -1,34 +1,37 @@
import { MODULE } from './constants.js'
-import { Logger } from './config.js'
-export class Utils {
+export let Utils = null
+
+Hooks.once('tokenActionHudCoreApiReady', async (coreModule) => {
+ Utils = class Utils {
/**
* Get setting value
* @param {string} key The key
* @param {string=null} defaultValue The default value
* @returns The setting value
*/
- static getSetting (key, defaultValue = null) {
- let value = defaultValue ?? null
- try {
- value = game.settings.get(MODULE.ID, key)
- } catch {
- Logger.debug(`Setting '${key}' not found`)
+ static getSetting (key, defaultValue = null) {
+ let value = defaultValue ?? null
+ try {
+ value = game.settings.get(MODULE.ID, key)
+ } catch {
+ coreModule.api.Logger.debug(`Setting '${key}' not found`)
+ }
+ return value
}
- return value
- }
- /**
+ /**
* Set setting value
* @param {string} key The key
* @param {string} value The value
*/
- static async setSetting (key, value) {
- try {
- value = await game.settings.set(MODULE.ID, key, value)
- Logger.debug(`Setting '${key}' set to '${value}'`)
- } catch {
- Logger.debug(`Setting '${key}' not found`)
+ static async setSetting (key, value) {
+ try {
+ value = await game.settings.set(MODULE.ID, key, value)
+ coreModule.api.Logger.debug(`Setting '${key}' set to '${value}'`)
+ } catch {
+ coreModule.api.Logger.debug(`Setting '${key}' not found`)
+ }
}
}
-}
+})