Merge pull request #4 from Oliver-Akins/feature/sheet-size-saving
Sheet Size Saving + Feature Flag Rework
This commit is contained in:
commit
9ea74b12c7
12 changed files with 190 additions and 49 deletions
21
README.md
21
README.md
|
|
@ -2,3 +2,24 @@
|
||||||
This is an intentionally bare-bones system that features a text-only character
|
This is an intentionally bare-bones system that features a text-only character
|
||||||
sheet, allowing the playing of games that may not otherwise have a Foundry system
|
sheet, allowing the playing of games that may not otherwise have a Foundry system
|
||||||
implementation.
|
implementation.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
There are not too many features included in this system for things like automation
|
||||||
|
as it's meant to be used to mostly play rules-light games. However there are some
|
||||||
|
special features that can be enabled on a per-world basis using a world script
|
||||||
|
to enable feature flags that you want.
|
||||||
|
|
||||||
|
### List of Feature Flags
|
||||||
|
| Flag | Description
|
||||||
|
| - | -
|
||||||
|
| `ROLL_MODE_CONTENT` | Allows players, GMs, and macros to send "blind" chat messages where only the GM gets to see the content.
|
||||||
|
| `STORABLE_SHEET_SIZE` | Makes it so that certain sheets are able to have their size saved, so that it resumes that size when opened.
|
||||||
|
|
||||||
|
### Example Feature Flag
|
||||||
|
In order to change these flags, you must make a world script that has something
|
||||||
|
like the below code in it:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// v- this is the name of the flag from the table above
|
||||||
|
taf.FEATURES.STORABLE_SHEET_SIZE = true;
|
||||||
|
```
|
||||||
|
|
@ -31,6 +31,7 @@ export default [
|
||||||
ActiveEffect: `readonly`,
|
ActiveEffect: `readonly`,
|
||||||
Dialog: `readonly`,
|
Dialog: `readonly`,
|
||||||
renderTemplate: `readonly`,
|
renderTemplate: `readonly`,
|
||||||
|
TextEditor: `readonly`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -42,6 +43,7 @@ export default [
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
globals: {
|
globals: {
|
||||||
Logger: `readonly`,
|
Logger: `readonly`,
|
||||||
|
taf: `readonly`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
|
@ -49,6 +51,7 @@ export default [
|
||||||
"func-names": [`warn`, `as-needed`],
|
"func-names": [`warn`, `as-needed`],
|
||||||
"grouped-accessor-pairs": `error`,
|
"grouped-accessor-pairs": `error`,
|
||||||
"no-alert": `error`,
|
"no-alert": `error`,
|
||||||
|
"no-empty": [`error`, { allowEmptyCatch: true }],
|
||||||
"no-implied-eval": `error`,
|
"no-implied-eval": `error`,
|
||||||
"no-invalid-this": `error`,
|
"no-invalid-this": `error`,
|
||||||
"no-lonely-if": `error`,
|
"no-lonely-if": `error`,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
export const FEATURE_FLAGS = Object.freeze({
|
|
||||||
ROLLMODECONTENT: `Roll Mode Message Content`,
|
|
||||||
});
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
import { FEATURE_FLAGS } from "../consts.mjs";
|
|
||||||
|
|
||||||
Hooks.on(`renderChatMessage`, (msg, html) => {
|
Hooks.on(`renderChatMessage`, (msg, html) => {
|
||||||
|
|
||||||
// Short-Circuit when the flag isn't set for the message
|
// Short-Circuit when the flag isn't set for the message
|
||||||
|
|
@ -7,13 +5,12 @@ Hooks.on(`renderChatMessage`, (msg, html) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const featureFlags = game.settings.get(game.system.id, `flags`);
|
const featureFlagEnabled = taf.FEATURES.ROLL_MODE_CONTENT;
|
||||||
const featureFlagEnabled = featureFlags.includes(FEATURE_FLAGS.ROLLMODECONTENT);
|
|
||||||
|
|
||||||
const contentElement = html.find(`.message-content`)[0];
|
const contentElement = html.find(`.message-content`)[0];
|
||||||
let content = contentElement.innerHTML;
|
let content = contentElement.innerHTML;
|
||||||
if (featureFlagEnabled && msg.blind && !game.user.isGM) {
|
if (featureFlagEnabled && msg.blind && !game.user.isGM) {
|
||||||
content = content.replace(/-=.*?=-/gm, `???`);
|
content = content.replace(/-=.*?=-/gm, `??`);
|
||||||
} else {
|
} else {
|
||||||
content = content.replace(/-=|=-/gm, ``);
|
content = content.replace(/-=|=-/gm, ``);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,9 @@ Hooks.once( `ready`, () => {
|
||||||
ui.sidebar.tabs[defaultTab].activate();
|
ui.sidebar.tabs[defaultTab].activate();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (game.settings.get(game.system.id, `devMode`)) {
|
||||||
|
console.log(`%cFeature Flags:`, `color: #00aa00; font-style: bold; font-size: 1.5rem;`);
|
||||||
|
Logger.table(taf.FEATURES);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,25 @@
|
||||||
export function registerDevSettings() {
|
export function registerDevSettings() {
|
||||||
|
const isLocalhost = window.location.hostname === `localhost`;
|
||||||
|
|
||||||
game.settings.register(game.system.id, `devMode`, {
|
game.settings.register(game.system.id, `devMode`, {
|
||||||
|
name: `Dev Mode?`,
|
||||||
scope: `client`,
|
scope: `client`,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
config: false,
|
config: isLocalhost,
|
||||||
default: false,
|
default: false,
|
||||||
requiresReload: true,
|
requiresReload: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
game.settings.register(game.system.id, `defaultTab`, {
|
game.settings.register(game.system.id, `defaultTab`, {
|
||||||
|
name: `Default Sidebar Tab`,
|
||||||
scope: `client`,
|
scope: `client`,
|
||||||
type: String,
|
type: String,
|
||||||
config: false,
|
config: isLocalhost,
|
||||||
requiresReload: false,
|
requiresReload: false,
|
||||||
|
onChange(value) {
|
||||||
|
if (!ui.sidebar.tabs[value]) {
|
||||||
|
ui.notifications.warn(`"${value}" cannot be found in the sidebar tabs, it may not work at reload.`);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,2 @@
|
||||||
import { FEATURE_FLAGS } from "../consts.mjs";
|
|
||||||
|
|
||||||
export function registerWorldSettings() {
|
export function registerWorldSettings() {
|
||||||
game.settings.register(game.system.id, `flags`, {
|
|
||||||
name: `Feature Flags`,
|
|
||||||
hint: `World-based feature flags that are used to enable/disable specific behaviours`,
|
|
||||||
scope: `world`,
|
|
||||||
type: new foundry.data.fields.SetField(
|
|
||||||
new foundry.data.fields.StringField(
|
|
||||||
{
|
|
||||||
empty: false,
|
|
||||||
trim: true,
|
|
||||||
options: Object.values(FEATURE_FLAGS),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
{
|
|
||||||
required: false,
|
|
||||||
initial: new Set(),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
config: true,
|
|
||||||
requiresReload: true,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
export class PlayerSheetv1 extends ActorSheet {
|
import { SizeStorable } from "../mixins/SizeStorable.mjs";
|
||||||
|
|
||||||
|
export class PlayerSheetv1 extends SizeStorable(ActorSheet) {
|
||||||
static get defaultOptions() {
|
static get defaultOptions() {
|
||||||
let opts = foundry.utils.mergeObject(
|
let opts = foundry.utils.mergeObject(
|
||||||
super.defaultOptions,
|
super.defaultOptions,
|
||||||
|
|
|
||||||
125
src/sheets/mixins/SizeStorable.mjs
Normal file
125
src/sheets/mixins/SizeStorable.mjs
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
import { DialogManager } from "../../utils/DialogManager.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This mixin allows making a class so that it can store the width/height data
|
||||||
|
* to the sheet or localhost in order to make using the text sheets a lil nicer.
|
||||||
|
*
|
||||||
|
* @param {ActorSheet|ItemSheet} cls The Sheet class to augment
|
||||||
|
* @returns The augmented class
|
||||||
|
*/
|
||||||
|
export function SizeStorable(cls) {
|
||||||
|
|
||||||
|
// Don't augment class when the feature isn't enabled
|
||||||
|
if (!taf.FEATURES.STORABLE_SHEET_SIZE) {
|
||||||
|
return cls;
|
||||||
|
}
|
||||||
|
|
||||||
|
return class SizeStorableClass extends cls {
|
||||||
|
constructor(doc, opts) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Find the saved size of the sheet, it takes the following order of precedence
|
||||||
|
from highest to lowest:
|
||||||
|
- Locally saved
|
||||||
|
- Default values on actor
|
||||||
|
- Default values from constructor
|
||||||
|
*/
|
||||||
|
/** @type {string|undefined} */
|
||||||
|
let size = localStorage.getItem(`${game.system.id}.size:${doc.uuid}`);
|
||||||
|
size ??= doc.getFlag(game.system.id, `size`);
|
||||||
|
|
||||||
|
// Apply the saved value to the options
|
||||||
|
if (size) {
|
||||||
|
const [ width, height ] = size.split(`,`);
|
||||||
|
opts.width = width;
|
||||||
|
opts.height = height;
|
||||||
|
};
|
||||||
|
|
||||||
|
super(doc, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
get hasLocalSize() {
|
||||||
|
return localStorage.getItem(`${game.system.id}.size:${this.object.uuid}`) != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
get hasGlobalSize() {
|
||||||
|
return this.object.getFlag(game.system.id, `size`) != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
_getHeaderButtons() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
class: `size-save`,
|
||||||
|
icon: `fa-solid fa-floppy-disk`,
|
||||||
|
label: `Save Size`,
|
||||||
|
onclick: () => {
|
||||||
|
|
||||||
|
const buttons = {
|
||||||
|
saveGlobal: {
|
||||||
|
label: `Save Global Size`,
|
||||||
|
callback: () => {
|
||||||
|
this.object.setFlag(
|
||||||
|
game.system.id,
|
||||||
|
`size`,
|
||||||
|
`${this.position.width},${this.position.height}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
saveLocal: {
|
||||||
|
label: `Save For Me Only`,
|
||||||
|
callback: () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
`${game.system.id}.size:${this.object.uuid}`,
|
||||||
|
`${this.position.width},${this.position.height}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add resets if there is a size already
|
||||||
|
if (this.hasGlobalSize) {
|
||||||
|
buttons.resetGlobal = {
|
||||||
|
label: `Reset Global Size`,
|
||||||
|
callback: () => {
|
||||||
|
this.object.unsetFlag(game.system.id, `size`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.hasLocalSize) {
|
||||||
|
buttons.resetLocal = {
|
||||||
|
label: `Reset Size For Me Only`,
|
||||||
|
callback: () => {
|
||||||
|
localStorage.removeItem(`${game.system.id}.size:${this.object.uuid}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// When a non-GM is using this system, we only want to save local sizes
|
||||||
|
if (!game.user.isGM) {
|
||||||
|
delete buttons.saveGlobal;
|
||||||
|
delete buttons.resetGlobal;
|
||||||
|
};
|
||||||
|
|
||||||
|
DialogManager.createOrFocus(
|
||||||
|
`${this.object.uuid}:size-save`,
|
||||||
|
{
|
||||||
|
title: `Save size of sheet: ${this.title}`,
|
||||||
|
content: `Saving the size of this sheet will cause it to open at the size it is when you press the save button`,
|
||||||
|
buttons,
|
||||||
|
render: (html) => {
|
||||||
|
const el = html[2];
|
||||||
|
el.style = `display: grid; grid-template-columns: 1fr 1fr; gap: 8px;`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
jQuery: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
...super._getHeaderButtons(),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
import { FEATURE_FLAGS } from "../../consts.mjs";
|
|
||||||
|
|
||||||
export function hideMessageText(content) {
|
export function hideMessageText(content) {
|
||||||
const featureFlags = game.settings.get(game.system.id, `flags`);
|
const hideContent = taf.FEATURES.ROLL_MODE_CONTENT;
|
||||||
const hideContent = featureFlags.includes(FEATURE_FLAGS.ROLLMODECONTENT);
|
|
||||||
if (hideContent) {
|
if (hideContent) {
|
||||||
return `-=${content}=-`;
|
return `-=${content}=-`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,18 @@
|
||||||
import { FEATURE_FLAGS } from "../consts.mjs";
|
|
||||||
import { hideMessageText } from "./feature_flags/rollModeMessageContent.mjs";
|
import { hideMessageText } from "./feature_flags/rollModeMessageContent.mjs";
|
||||||
|
|
||||||
globalThis.taf = Object.freeze({
|
Object.defineProperty(
|
||||||
utils: {
|
globalThis,
|
||||||
|
`taf`,
|
||||||
|
{
|
||||||
|
value: Object.freeze({
|
||||||
|
utils: Object.freeze({
|
||||||
hideMessageText,
|
hideMessageText,
|
||||||
|
}),
|
||||||
|
FEATURES: Object.preventExtensions({
|
||||||
|
ROLL_MODE_CONTENT: false,
|
||||||
|
STORABLE_SHEET_SIZE: false,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
writable: false,
|
||||||
},
|
},
|
||||||
const: {
|
);
|
||||||
FEATURE_FLAGS,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "taf",
|
"id": "taf",
|
||||||
"title": "Text-Based Actors",
|
"title": "Text-Based Actors",
|
||||||
"description": "",
|
"description": "",
|
||||||
"version": "1.2.0",
|
"version": "2.0.0",
|
||||||
"download": "https://github.com/Oliver-Akins/Text-Actors-Foundry/releases/latest/download/dotdungeon.zip",
|
"download": "https://github.com/Oliver-Akins/Text-Actors-Foundry/releases/latest/download/dotdungeon.zip",
|
||||||
"manifest": "https://github.com/Oliver-Akins/Text-Actors-Foundry/releases/latest/download/system.json",
|
"manifest": "https://github.com/Oliver-Akins/Text-Actors-Foundry/releases/latest/download/system.json",
|
||||||
"url": "https://github.com/Oliver-Akins/Text-Actors-Foundry",
|
"url": "https://github.com/Oliver-Akins/Text-Actors-Foundry",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue