Compare commits
No commits in common. "383f66e2979333118dad6651c35239532f3be6d2" and "f617b26fe17b769afeabbd97065bdabd1f41487e" have entirely different histories.
383f66e297
...
f617b26fe1
6 changed files with 115 additions and 249 deletions
36
README.md
36
README.md
|
@ -1,4 +1,4 @@
|
||||||
# The Mill's Fabula - v0.3.0
|
# The Mill's Fabula - v0.2.0
|
||||||
|
|
||||||
This little [FoundryVTT](https://foundryvtt.com/) module is a collection of compendiums and functionalities
|
This little [FoundryVTT](https://foundryvtt.com/) module is a collection of compendiums and functionalities
|
||||||
to power our Fabula Ultima campaigns.
|
to power our Fabula Ultima campaigns.
|
||||||
|
@ -31,31 +31,25 @@ highlighting the tokens that have played for this combat turn.
|
||||||
The module supports going back and forth in the combat rounds, as well as going back in the turn order.
|
The module supports going back and forth in the combat rounds, as well as going back in the turn order.
|
||||||
(Though because of limitations of the Fabula Ultima system, does not allow going *forward* in the turn order.)
|
(Though because of limitations of the Fabula Ultima system, does not allow going *forward* in the turn order.)
|
||||||
|
|
||||||
### Settings
|
## Settings
|
||||||
|
|
||||||
- An image to use for the default/idle border
|
- An image to use for the default/idle border
|
||||||
- An image to use for the took turn/played/inactive border
|
- An image to use for the took turn/played/inactive border
|
||||||
|
|
||||||
### Limitations
|
## Limitations
|
||||||
|
|
||||||
There are currently one minor issue that might be fixed :
|
There are currently two main issues that need to be fixed :
|
||||||
1. The token borders will be incorrect when switching between different encounters in the same scene
|
1. The tokens will not be updated when the GM is not on the scene.
|
||||||
- Producing a combat event or switching away and back to the scene will fix it.
|
- Indeed, the Fabula Ultima system seems to prevent players from receiving combat events,
|
||||||
- It doesn't appear that there is an event on combat switch that could be hooked into, which makes fixing the
|
so the GM is the only one that can receive them and update the tokens. That means they need to be in the active
|
||||||
issue uncertain.
|
combat scene for the changes to take effect.
|
||||||
|
- However, given that the module only uses the current state, if the GM comes back to the scene and a combat event is
|
||||||
## Token UI adjustments
|
triggered, the token borders will become correct.
|
||||||
|
2. The token borders will be incorrect when switching to a new scene
|
||||||
Given that we add a border *on* the tokens, it conflicts with the base attribute bars which are drawn over the token's
|
- It is unclear why, but apparently switching to another scene is very different from loading a new scene,
|
||||||
square.
|
and the combat encounter of the scene is not available when tokens are created. This means that the combat status,
|
||||||
The token UI adjustments move the two attribute bars below the token, outside its space, first HP then mana.
|
and thus the border type, cannot be properly determined on scene switch.
|
||||||
As this is where the nameplate of the token should be, move it above the token instead.
|
- This can be fixed by receiving a combat update, either from the players or the GM, on the scene.
|
||||||
|
|
||||||
### Limitations
|
|
||||||
|
|
||||||
- The token's detailed UI when right-clicking will overlap the bars in their new positions (it already overlapped the name)
|
|
||||||
- The current implementation relies on monkey patching, which make it vulnerable to compatibility issues with
|
|
||||||
other modules manipulating the same methods.
|
|
||||||
|
|
||||||
# Compendia
|
# Compendia
|
||||||
|
|
||||||
|
|
|
@ -17,14 +17,6 @@
|
||||||
"Hint": "The image to use as a border for player tokens that have taken their turn."
|
"Hint": "The image to use as a border for player tokens that have taken their turn."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"TokenUiAdjust": {
|
|
||||||
"Settings": {
|
|
||||||
"Enabled": {
|
|
||||||
"Name": "Token UI adjustments",
|
|
||||||
"Hint": "Moves both attribute bars below the token and the nameplate above."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,14 +17,6 @@
|
||||||
"Hint": "L'image à utiliser comme bordure pour les jetons joueurs qui ont joué leur tour."
|
"Hint": "L'image à utiliser comme bordure pour les jetons joueurs qui ont joué leur tour."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"TokenUiAdjust": {
|
|
||||||
"Settings": {
|
|
||||||
"Enabled": {
|
|
||||||
"Name": "Ajustements d'interface des tokens",
|
|
||||||
"Hint": "Déplace les barres d'attributs sous le token et le nom au dessus."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
module.json
16
module.json
|
@ -2,7 +2,7 @@
|
||||||
"id": "the-mills-fabula",
|
"id": "the-mills-fabula",
|
||||||
"title": "Fabula Moletrina",
|
"title": "Fabula Moletrina",
|
||||||
"description": "A module implementing our idiosyncratic ideas for the Mill's campaigns.",
|
"description": "A module implementing our idiosyncratic ideas for the Mill's campaigns.",
|
||||||
"version": "0.3.0",
|
"version": "0.2.0",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"minimum": "12",
|
"minimum": "12",
|
||||||
"verified": "12",
|
"verified": "12",
|
||||||
|
@ -50,11 +50,9 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"esmodules": [
|
"esmodules": [
|
||||||
"scripts/token_combat_border.mjs",
|
"scripts/token_combat_border.mjs"
|
||||||
"scripts/token_ui_adjust.mjs"
|
|
||||||
],
|
],
|
||||||
"socket": true,
|
"socket": true,
|
||||||
|
|
||||||
"packs": [
|
"packs": [
|
||||||
{
|
{
|
||||||
"name": "Macros",
|
"name": "Macros",
|
||||||
|
@ -69,18 +67,10 @@
|
||||||
"flags": {}
|
"flags": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packFolders": [
|
|
||||||
{
|
|
||||||
"name": "Fabula Moletrina",
|
|
||||||
"sorting": "a",
|
|
||||||
"color": "#d3b719",
|
|
||||||
"packs": ["Macros"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
"license": "LICENSE",
|
"license": "LICENSE",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"url": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/src/branch/release",
|
"url": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/src/branch/release",
|
||||||
"manifest": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/raw/branch/release/module.json",
|
"manifest": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/raw/branch/release/module.json",
|
||||||
"download": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/archive/v0.3.0.zip"
|
"download": "https://git.tfk-astrodome.net/trotFunky/TheMillsFabula/archive/v0.2.0.zip"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import * as MillsFabula from "./mills_fabula.mjs";
|
import * as MillsFabula from "./mills_fabula.mjs";
|
||||||
import { FUHooks } from "/systems/projectfu/module/hooks.mjs"
|
import { FUHooks } from "/systems/projectfu/module/hooks.mjs"
|
||||||
import { FU } from "/systems/projectfu/module/helpers/config.mjs"
|
import { FU } from "/systems/projectfu/module/helpers/config.mjs"
|
||||||
import { FUCombat } from '/systems/projectfu/module/ui/combat.mjs';
|
|
||||||
|
|
||||||
// NOTES
|
// NOTES
|
||||||
// Tokens are working PIXI.js objects, can .addChild() a sprite
|
// Tokens are working PIXI.js objects, can .addChild() a sprite
|
||||||
|
@ -15,8 +14,7 @@ import { FUCombat } from '/systems/projectfu/module/ui/combat.mjs';
|
||||||
*/
|
*/
|
||||||
let socket;
|
let socket;
|
||||||
const SocketMessages = Object.freeze({
|
const SocketMessages = Object.freeze({
|
||||||
CombatUpdateBorder: "combat-update-border",
|
UpdateBorder: "update-border",
|
||||||
SetBorder: "set-border",
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const BorderSettings = Object.freeze({
|
const BorderSettings = Object.freeze({
|
||||||
|
@ -80,14 +78,16 @@ function create_new_border(type, width, height, visible) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle the borders of a token.
|
* Toggle the borders of a token by ID, as we cannot pass the Token object via SocketLib (most objects being recursive).
|
||||||
* @param {Token} token The token to update
|
* @param {string} token_id The ID of the token to update
|
||||||
* @param {boolean} played Status of the token to update
|
* @param {boolean} played Status of the token to update
|
||||||
*/
|
*/
|
||||||
function token_set_border_visibility(token, played) {
|
function token_set_border_visibility(token_id, played) {
|
||||||
|
let token = canvas.scene?.tokens.get(token_id).object;
|
||||||
// Get the borders we manually added in the `drawToken` hook, if it has any.
|
// Get the borders we manually added in the `drawToken` hook, if it has any.
|
||||||
let borders = token.children.filter((child) => child.borderType);
|
let borders = token.children.filter((child) => child.borderType);
|
||||||
|
// console.debug("↓↓↓ set_border_visibility ↓↓↓");
|
||||||
|
// console.debug(token)
|
||||||
for (let border of borders) {
|
for (let border of borders) {
|
||||||
if (border.borderType === BorderTypes.Active) {
|
if (border.borderType === BorderTypes.Active) {
|
||||||
border.visible = !played;
|
border.visible = !played;
|
||||||
|
@ -95,105 +95,39 @@ function token_set_border_visibility(token, played) {
|
||||||
border.visible = played;
|
border.visible = played;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// console.debug("↑↑↑ set_border_visibility ↑↑↑");
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper around `token_set_border_visibility()` to be invoked via SocketLib.
|
|
||||||
* It checks that the client is on the proper scene, otherwise the Token wouldn't be found.
|
|
||||||
*
|
|
||||||
* @param {string} scene_id The ID of the scene of the token to update
|
|
||||||
* @param {string} token_id The ID of the token to update
|
|
||||||
* @param {boolean} played Should the visible borders be the base or the played one ?
|
|
||||||
*/
|
|
||||||
function token_remote_set_border_visibility(scene_id, token_id, played) {
|
|
||||||
/*
|
|
||||||
* Check that we are on the same scene as the token being updated,
|
|
||||||
* otherwise it won't exist for us.
|
|
||||||
*/
|
|
||||||
if (scene_id !== canvas.scene?.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
token_set_border_visibility(canvas.scene?.tokens.get(token_id).object, played)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a token has played in a specific round, or the current combat round if it exists for the token.
|
* Check if a token has played in a specific round, or the current combat round if it exists for the token.
|
||||||
* The function does not rely on the token's internal data, as it is not properly set when changing scenes.
|
|
||||||
* @param {Token} token The actual token object to check combat status
|
* @param {Token} token The actual token object to check combat status
|
||||||
* @param {number} [round] The round to check the token's status in, will check the token's combat current turn otherwise
|
* @param {number} [round] The round to check the token's status in, will check the token's combat current turn otherwise
|
||||||
* @returns {boolean} If the token has played in the current or requested round
|
* @returns {boolean} If the token has played in the current or requested round
|
||||||
*/
|
*/
|
||||||
function token_has_played(token, round = -1) {
|
function token_has_played(token, round = -1) {
|
||||||
|
// console.debug("↓↓↓ token_has_played ↓↓↓");
|
||||||
|
// console.debug(token.inCombat)
|
||||||
|
if (!token.inCombat) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When we change scene, the token might not be updated with the combat
|
* As we check beforehand, combat should always exist.
|
||||||
* data, so we might need to go check ourselves if they are not set.
|
* @type {Combat}
|
||||||
* @type {FUCombat}
|
|
||||||
*/
|
*/
|
||||||
let combat = token.combatant?.combat;
|
let combat = token.combatant.combat;
|
||||||
/** @type {Combatant} */
|
|
||||||
let combatant = token.combatant;
|
|
||||||
|
|
||||||
// We might be changing scene, so let's check if that's true.
|
|
||||||
if (!combatant) {
|
|
||||||
/*
|
|
||||||
* There should only be one active combat per scene.
|
|
||||||
* If there isn't one, we're sure that the token is not in combat.
|
|
||||||
*/
|
|
||||||
let active_scene_combat = game.combats.contents.filter(
|
|
||||||
(combat) => combat.active && combat.scene.id === token.scene.id)
|
|
||||||
if (active_scene_combat.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
combat = active_scene_combat[0];
|
|
||||||
|
|
||||||
// Now search among the combatants
|
|
||||||
for (let active_combatant of combat.combatants) {
|
|
||||||
if (active_combatant.tokenId === token.id) {
|
|
||||||
combatant = active_combatant;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// We have not found our token in the active combat, assume it isn't in combat.
|
|
||||||
if (!combatant) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let round_turns_taken = combat?.flags.projectfu.CombatantsTurnTaken[round < 0 ? combat?.round : round]
|
let round_turns_taken = combat?.flags.projectfu.CombatantsTurnTaken[round < 0 ? combat?.round : round]
|
||||||
|
// console.debug(`Testing played for round : ${combat?.round}`)
|
||||||
|
console.debug(round_turns_taken)
|
||||||
|
|
||||||
// No token has taken a turn, or all turns were reverted.
|
// No token has taken a turn, or all turns were reverted.
|
||||||
if (!round_turns_taken || round_turns_taken.length === 0) {
|
if (!round_turns_taken || round_turns_taken.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// console.debug("↑↑↑ token_has_played ↑↑↑");
|
||||||
// Token might have played, let's search now.
|
// Token might have played, let's search now.
|
||||||
return round_turns_taken.includes(combatant.id)
|
return round_turns_taken.includes(token.combatant.id)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wrapper function around `token_set_border_visibility()` that is called via SocketLib for combat updates.
|
|
||||||
* It is executed by every client, which allows us to check if we are on the proper scene, ensuring that
|
|
||||||
* we can find the token on each relevant client, rather than only on the GM's side which doesn't
|
|
||||||
* work if they are not in the combat scene.
|
|
||||||
*
|
|
||||||
* Scene and Token are passed by IDs as the Foundry objects are recursive and cannot be passed via SocketLib.
|
|
||||||
* @param {string} scene_id The ID of the scene of the token to update
|
|
||||||
* @param {string} token_id The ID of the token to update
|
|
||||||
* @param {number} round The combat round relating to the update
|
|
||||||
*/
|
|
||||||
function token_combat_visibility_remote_update(scene_id, token_id, round) {
|
|
||||||
/*
|
|
||||||
* Check that we are on the same scene as the token being updated,
|
|
||||||
* otherwise it won't exist for us.
|
|
||||||
*/
|
|
||||||
if (scene_id !== canvas.scene?.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let token = canvas.scene?.tokens.get(token_id).object;
|
|
||||||
token_set_border_visibility(token, token_has_played(token, round))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -203,61 +137,21 @@ function token_combat_visibility_remote_update(scene_id, token_id, round) {
|
||||||
*/
|
*/
|
||||||
function combat_hook_update_token_borders(combat, data) {
|
function combat_hook_update_token_borders(combat, data) {
|
||||||
// Turns is the array of *tokens having turns in this encounter*.
|
// Turns is the array of *tokens having turns in this encounter*.
|
||||||
|
// console.debug("↓↓↓ combat_hook_update_token_borders ↓↓↓");
|
||||||
|
// console.debug(combat)
|
||||||
|
// console.debug(data)
|
||||||
for (let combatant of combat.turns) {
|
for (let combatant of combat.turns) {
|
||||||
// The combat passed by the hook is still on the previous round or turn, which would make the check
|
// The combat passed by the hook still is on the previous round or turn, which would make the check
|
||||||
// use the previous round rather than the new one. Use the round contained in data instead,
|
// use the previous round rather than the new one. Use the round contained in data instead, which is always
|
||||||
// which is always the new one.
|
// the new one.
|
||||||
socket.executeForEveryone(SocketMessages.CombatUpdateBorder,
|
socket.executeForEveryone(SocketMessages.UpdateBorder, combatant.token.id,
|
||||||
combatant.sceneId, combatant.tokenId, data.round).then();
|
token_has_played(combatant?.token.object, data.round)).then();
|
||||||
}
|
}
|
||||||
|
// console.debug("↑↑↑ combat_hook_update_token_borders ↑↑↑");
|
||||||
}
|
}
|
||||||
|
|
||||||
function combat_border_main() {
|
function combat_border_main() {
|
||||||
socket.register(SocketMessages.CombatUpdateBorder, token_combat_visibility_remote_update);
|
Hooks.once("init", () => {
|
||||||
socket.register(SocketMessages.SetBorder, token_remote_set_border_visibility);
|
|
||||||
|
|
||||||
// Create the borders from defined textures and add them to the tokens when they are first created on canvas.
|
|
||||||
Hooks.on("drawToken", (drawn_token) => {
|
|
||||||
// Only apply the borders to player tokens
|
|
||||||
if (drawn_token.actor?.type !== "character")
|
|
||||||
return;
|
|
||||||
|
|
||||||
let has_played_turn = token_has_played(drawn_token);
|
|
||||||
const token_size = drawn_token.getSize();
|
|
||||||
drawn_token.addChild(create_new_border(BorderTypes.Active, token_size.width, token_size.height, !has_played_turn));
|
|
||||||
drawn_token.addChild(create_new_border(BorderTypes.Played, token_size.width, token_size.height, has_played_turn));
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
Hooks.on("ready", () => {
|
|
||||||
// Players cannot run the combat hooks used here, which trigger for the GM no matter what.
|
|
||||||
// So register them for the GM only, who will execute the updates for players via SocketLib.
|
|
||||||
if (!game.user?.isGM) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Hooks.on("combatTurn", combat_hook_update_token_borders);
|
|
||||||
Hooks.on("combatRound", combat_hook_update_token_borders);
|
|
||||||
|
|
||||||
// No Foundry hook on end of combat, so use Fabula Ultima's.
|
|
||||||
Hooks.on(FUHooks.COMBAT_EVENT, (combat_event) => {
|
|
||||||
if (combat_event.type === FU.combatEvent.endOfCombat) {
|
|
||||||
for (let combatant of combat_event.combatants) {
|
|
||||||
// End of combat, clear all tokens.
|
|
||||||
socket.executeForEveryone(SocketMessages.SetBorder,
|
|
||||||
combatant.sceneId, combatant.tokenId, false).then();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to setup socketlib early, doing it in the `init` hook is too late.
|
|
||||||
Hooks.once("socketlib.ready", () => {
|
|
||||||
socket = socketlib.registerModule(MillsFabula.id);
|
|
||||||
})
|
|
||||||
|
|
||||||
Hooks.once("init", () => {
|
|
||||||
game.settings.register(MillsFabula.id, BorderSettings.BorderEnabled, {
|
game.settings.register(MillsFabula.id, BorderSettings.BorderEnabled, {
|
||||||
name: game.i18n.localize('MF.Border.Settings.BorderEnabled.Name'),
|
name: game.i18n.localize('MF.Border.Settings.BorderEnabled.Name'),
|
||||||
hint: game.i18n.localize('MF.Border.Settings.BorderEnabled.Hint'),
|
hint: game.i18n.localize('MF.Border.Settings.BorderEnabled.Hint'),
|
||||||
|
@ -292,13 +186,70 @@ Hooks.once("init", () => {
|
||||||
default: 'modules/the-mills-fabula/assets/default-borders/played.webp'
|
default: 'modules/the-mills-fabula/assets/default-borders/played.webp'
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!game.settings.get(MillsFabula.id, BorderSettings.BorderEnabled)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the border textures based on the image chose in the settings.
|
// Create the border textures based on the image chose in the settings.
|
||||||
base_border = PIXI.BaseTexture.from(game.settings.get(MillsFabula.id, BorderSettings.BaseBorderPath));
|
base_border = PIXI.BaseTexture.from(game.settings.get(MillsFabula.id, BorderSettings.BaseBorderPath));
|
||||||
played_border = PIXI.BaseTexture.from(game.settings.get(MillsFabula.id, BorderSettings.PlayedBorderPath));
|
played_border = PIXI.BaseTexture.from(game.settings.get(MillsFabula.id, BorderSettings.PlayedBorderPath));
|
||||||
|
});
|
||||||
|
|
||||||
combat_border_main()
|
Hooks.once("socketlib.ready", () => {
|
||||||
});
|
socket = socketlib.registerModule(MillsFabula.id);
|
||||||
|
socket.register(SocketMessages.UpdateBorder, token_set_border_visibility);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create the borders from defined textures and add them to the tokens when they are first created on canvas.
|
||||||
|
// FIXME: Does not work on scene change !
|
||||||
|
Hooks.on("drawToken", (drawn_token) => {
|
||||||
|
// FIXME: Handle deactivation properly
|
||||||
|
if (!game.settings.get(MillsFabula.id, BorderSettings.BorderEnabled)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Only apply the borders to player tokens
|
||||||
|
if (drawn_token.actor?.type !== "character")
|
||||||
|
return;
|
||||||
|
|
||||||
|
let has_played_turn = token_has_played(drawn_token);
|
||||||
|
// console.log(drawn_token)
|
||||||
|
// console.log(`Is in combat ? ${drawn_token.inCombat}`)
|
||||||
|
// console.log(`Combatant ? ${drawn_token.combatant}`)
|
||||||
|
// console.log(`Combat ?`)
|
||||||
|
// console.log(drawn_token.combatant.combat);
|
||||||
|
// console.log(game.combats.combats)
|
||||||
|
|
||||||
|
const token_size = drawn_token.getSize();
|
||||||
|
drawn_token.addChild(create_new_border(BorderTypes.Active, token_size.width, token_size.height, !has_played_turn));
|
||||||
|
drawn_token.addChild(create_new_border(BorderTypes.Played, token_size.width, token_size.height, has_played_turn));
|
||||||
|
|
||||||
|
// console.log("============")
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
Hooks.on("ready", () => {
|
||||||
|
// FIXME: Handle deactivation properly
|
||||||
|
if (!game.settings.get(MillsFabula.id, BorderSettings.BorderEnabled)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Players cannot run the combat hooks used here, which trigger for the GM no matter what.
|
||||||
|
// So register them for the GM only, who will execute the updates for players via SocketLib.
|
||||||
|
if (!game.user?.isGM) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.debug("↓↓↓ Registering ↓↓↓")
|
||||||
|
Hooks.on("combatTurn", combat_hook_update_token_borders);
|
||||||
|
Hooks.on("combatRound", combat_hook_update_token_borders);
|
||||||
|
// console.debug("↑↑↑ Registering ↑↑↑")
|
||||||
|
|
||||||
|
// No Foundry hook on end of combat, so use Fabula Ultima's.
|
||||||
|
// FIXME: Does not work when the GM is in another scene, as they don't receive the event.
|
||||||
|
Hooks.on(FUHooks.COMBAT_EVENT, (combat_event) => {
|
||||||
|
if (combat_event.type === FU.combatEvent.endOfCombat) {
|
||||||
|
for (let combatant of combat_event.combatants) {
|
||||||
|
// End of combat, clear all tokens.
|
||||||
|
socket.executeForEveryone(SocketMessages.UpdateBorder, combatant.token?.id, false).then();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
combat_border_main()
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
import * as MillsFabula from "./mills_fabula.mjs";
|
|
||||||
|
|
||||||
const TokenUiSettings = Object.freeze({
|
|
||||||
Enabled: "uiAdjustEnabled",
|
|
||||||
})
|
|
||||||
|
|
||||||
Hooks.once("setup", () => {
|
|
||||||
game.settings.register(MillsFabula.id, TokenUiSettings.Enabled, {
|
|
||||||
name: "MF.TokenUiAdjust.Settings.Enabled.Name",
|
|
||||||
hint: "MF.TokenUiAdjust.Settings.Enabled.Hint",
|
|
||||||
type: Boolean,
|
|
||||||
config: true,
|
|
||||||
scope: "world",
|
|
||||||
requiresReload: true,
|
|
||||||
default: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!game.settings.get(MillsFabula.id, TokenUiSettings.Enabled)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Padding above and below the token, percentage of a grid square. */
|
|
||||||
const top_bot_padding = 5;
|
|
||||||
|
|
||||||
Hooks.on("drawToken", (drawn_token) => {
|
|
||||||
/*
|
|
||||||
* Drawing attribute bars and nameplates are handled by private functions called
|
|
||||||
* after the `drawToken` hook. That means that we cannot change their position in this hook.
|
|
||||||
*
|
|
||||||
* We could update them in the `refreshToken` hook, which works, but is called a lot more than
|
|
||||||
* necessary and needs a view update (move the view) from the canvas to show the updates.
|
|
||||||
*
|
|
||||||
* Instead : we can replace the draw functions handling the bars and nameplate.
|
|
||||||
* They are still useful, so make of copy bound to the drawn token so that we can call them
|
|
||||||
* in our replacement function, then make the changes that we need.
|
|
||||||
*/
|
|
||||||
let base_drawBar = drawn_token._drawBar.bind(drawn_token);
|
|
||||||
drawn_token._drawBar = (number, bar, data) => {
|
|
||||||
const token_height = drawn_token.getSize().height
|
|
||||||
const padding = drawn_token.scene.grid.size * top_bot_padding/100;
|
|
||||||
base_drawBar(number, bar, data)
|
|
||||||
bar.position.set(0, token_height + padding + number * bar.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
let base_refreshNameplate = drawn_token._refreshNameplate.bind(drawn_token);
|
|
||||||
drawn_token._refreshNameplate = () => {
|
|
||||||
const token_width = drawn_token.getSize().width;
|
|
||||||
const padding = drawn_token.scene.grid.size * top_bot_padding/100;
|
|
||||||
base_refreshNameplate()
|
|
||||||
drawn_token.nameplate.position.set(token_width/2, -(padding + drawn_token.nameplate.height))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
Loading…
Add table
Add a link
Reference in a new issue