465 lines
19 KiB
JavaScript
465 lines
19 KiB
JavaScript
import { GeneralSettings } from "../../interface/GeneralSettings.js";
|
|
import { ObjectsInteractionsFX as OIF } from "../../ObjectsInteractionsFX.js";
|
|
|
|
export class SettingsSkeleton extends FormApplication
|
|
{
|
|
static get defaultOptions() {
|
|
const DefaultOptions = super.defaultOptions;
|
|
|
|
const OverrideOptions = {
|
|
closeOnSubmit: false,
|
|
height: 'auto',
|
|
with: 600,
|
|
submitOnChange: true,
|
|
template: OIF.TEMPLATES.SETTINGS_SKELETON
|
|
};
|
|
|
|
const MergedOptions = foundry.utils.mergeObject(DefaultOptions, OverrideOptions);
|
|
return MergedOptions;
|
|
}
|
|
|
|
static Settings = {}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Prototype function used to register custom settings
|
|
////////////////////////////////////////////////////////////
|
|
static _protoRegister(config, name, options)
|
|
{
|
|
if (SettingsSkeleton.Settings[config] == undefined)
|
|
{
|
|
SettingsSkeleton.Settings[config] = {};
|
|
}
|
|
|
|
// Prepare the internal foundry setting options
|
|
let FoundrySettingOptions = {
|
|
name: options['name'],
|
|
hint: options['hint'],
|
|
scope: options['scope'],
|
|
default: options['default'],
|
|
config: false
|
|
}
|
|
|
|
// Detect and convert custom type to be used by foundry
|
|
if (options['type'] != undefined)
|
|
{
|
|
// Sets a value inside option, named after the type name, as true
|
|
// This is used inside .hbs templates to select which option
|
|
// display/editor will be shown
|
|
options[options['type']] = true;
|
|
|
|
// Convert type to a foundry compatible
|
|
switch (options['type'])
|
|
{
|
|
case 'checkbox':
|
|
FoundrySettingOptions['type'] = Boolean;
|
|
break;
|
|
|
|
case 'slider':
|
|
FoundrySettingOptions['type'] = Number;
|
|
FoundrySettingOptions['range'] = options['range'];
|
|
break;
|
|
|
|
case 'string':
|
|
FoundrySettingOptions['type'] = String;
|
|
break;
|
|
|
|
case 'dropdown':
|
|
FoundrySettingOptions['type'] = String;
|
|
FoundrySettingOptions['choices'] = options['choices'];
|
|
break;
|
|
|
|
default:
|
|
console.error(`Cannot register setting, ${options['type']} is not a valid type`);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Register setting inside foundry
|
|
game.settings.register(OIF.ID, name, FoundrySettingOptions);
|
|
|
|
// Get currently set value to be displayed
|
|
options['value'] = game.settings.get(OIF.ID, name);
|
|
|
|
// Dropdown types are handled differently
|
|
if (options['type'] == 'dropdown')
|
|
{
|
|
// Iterate through the choices and set them as selected
|
|
// or not
|
|
options['choices'].forEach((element) => {
|
|
if (element.value == options['value'])
|
|
{
|
|
element['selected'] = true;
|
|
}
|
|
else
|
|
{
|
|
element['selected'] = false;
|
|
}
|
|
})
|
|
}
|
|
|
|
// Set the ID to be used as reference later
|
|
options['referenceID'] = name;
|
|
|
|
// Check there are required modules
|
|
let RequiredModule = options['requiredModule'];
|
|
if (RequiredModule != undefined)
|
|
{
|
|
// Check if the required module is active
|
|
if (!RequiredModule.active)
|
|
{
|
|
options['disabled'] = true;
|
|
options['disabledMessage'] = game.i18n.localize('OIF.Settings.ModuleRequired').replace("${module}", `"${RequiredModule.name}"`);
|
|
}
|
|
}
|
|
|
|
// Register inside OIF
|
|
SettingsSkeleton.Settings[config][name] = options;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Prototype function used to get custom settings
|
|
////////////////////////////////////////////////////////////
|
|
static _protoGet(config, name)
|
|
{
|
|
let CurrentSetting = SettingsSkeleton.Settings[config][name];
|
|
switch (CurrentSetting['type'])
|
|
{
|
|
case 'checkbox':
|
|
return CurrentSetting['value'] && !CurrentSetting['disabled'];
|
|
break;
|
|
|
|
case 'slider':
|
|
case 'string':
|
|
case 'dropdown':
|
|
return CurrentSetting['disabled'] ? CurrentSetting['default'] : CurrentSetting['value'];
|
|
break;
|
|
|
|
default:
|
|
return CurrentSetting['value'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Internal function that checks if restart is required
|
|
////////////////////////////////////////////////////////////
|
|
async _checkForRestart(config, id)
|
|
{
|
|
if (SettingsSkeleton.Settings[config][id]['restart'] != null && SettingsSkeleton.Settings[config][id]['restart'] != undefined && SettingsSkeleton.Settings[config][id]['restart'])
|
|
{
|
|
SettingsSkeleton.Settings[config][id]['requiresRestart'] = true;
|
|
SettingsSkeleton.Settings[config][id]['restartMessage'] = 'OIF.Settings.RestartRequired';
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Internal function for handling Checkbox interactions
|
|
////////////////////////////////////////////////////////////
|
|
async _handleCheckBoxInteraction(config, event)
|
|
{
|
|
let ClickedElement = $(event.currentTarget)[0];
|
|
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.checked);
|
|
SettingsSkeleton.Settings[config][ClickedElement.id]['value'] = ClickedElement.checked;
|
|
|
|
this._checkForRestart(config, ClickedElement.id);
|
|
SettingsSkeleton._protoUpdateSettings(config);
|
|
this.render();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Internal function for handling Slider interactions
|
|
////////////////////////////////////////////////////////////
|
|
async _handleSliderInteraction(config, event)
|
|
{
|
|
let ClickedElement = $(event.currentTarget)[0];
|
|
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.value);
|
|
|
|
// Check value
|
|
let CurrentValue = Number(ClickedElement.value);
|
|
if (CurrentValue > ClickedElement.max)
|
|
{
|
|
CurrentValue = ClickedElement.max;
|
|
}
|
|
else if (CurrentValue < ClickedElement.min)
|
|
{
|
|
CurrentValue = ClickedElement.min;
|
|
}
|
|
|
|
SettingsSkeleton.Settings[config][ClickedElement.id]['value'] = CurrentValue;
|
|
|
|
this._checkForRestart(config, ClickedElement.id);
|
|
SettingsSkeleton._protoUpdateSettings(config);
|
|
this.render();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Internal function for handling String interactions
|
|
////////////////////////////////////////////////////////////
|
|
async _handleStringInteraction(config, event)
|
|
{
|
|
let ClickedElement = $(event.currentTarget)[0];
|
|
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.value);
|
|
SettingsSkeleton.Settings[config]['value'] = ClickedElement.value;
|
|
|
|
this._checkForRestart(config, ClickedElement.id);
|
|
SettingsSkeleton._protoUpdateSettings(config);
|
|
this.render();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Internal function for handling Dropdown interactions
|
|
////////////////////////////////////////////////////////////
|
|
async _handleDropdownInteraction(config, event)
|
|
{
|
|
let ClickedElement = $(event.currentTarget)[0];
|
|
game.settings.set(OIF.ID, ClickedElement.id, ClickedElement.value);
|
|
SettingsSkeleton.Settings[config][ClickedElement.id]['value'] = ClickedElement.value;
|
|
|
|
SettingsSkeleton.Settings[config][ClickedElement.id]['choices'].forEach(function(element)
|
|
{
|
|
element['selected'] = false;
|
|
if (element.value == ClickedElement.value)
|
|
{
|
|
element['selected'] = true;
|
|
}
|
|
});
|
|
|
|
this._checkForRestart(config, ClickedElement.id);
|
|
SettingsSkeleton._protoUpdateSettings(config);
|
|
this.render();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Prototype function that updates settings
|
|
////////////////////////////////////////////////////////////
|
|
static _protoUpdateSettings(config)
|
|
{
|
|
for (const key in SettingsSkeleton.Settings[config])
|
|
{
|
|
// Check if current element has a dependecy
|
|
let CurrentSetting = SettingsSkeleton.Settings[config][key];
|
|
let CurrentDependencies = CurrentSetting['dependsOn'];
|
|
let CurrentUndependencies = CurrentSetting['excludesOn'];
|
|
|
|
// Check if setting requires a module to be used
|
|
let RequiredModule = CurrentSetting.requiredModule;
|
|
if (RequiredModule != null && RequiredModule != undefined)
|
|
{
|
|
// Check if required module isn't loaded
|
|
if (!RequiredModule.active)
|
|
{
|
|
CurrentSetting['disabled'] = true;
|
|
CurrentSetting['disabledMessage'] = game.i18n.localize('OIF.Settings.ModuleRequired').replace("${module}", `"${RequiredModule.name}"`);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Check if setting has dependencies
|
|
let AllRequirementsMet = true;
|
|
if (CurrentDependencies != null && CurrentDependencies != undefined)
|
|
{
|
|
// Loop through all dependencies to check if the are Boolean
|
|
let BoolCheck = true;
|
|
for (let index = 0; index < CurrentDependencies.length; index++)
|
|
{
|
|
// Check if the current dependency is a Boolean
|
|
const CurrentDependency = CurrentDependencies[index];
|
|
if (SettingsSkeleton.Settings[config][CurrentDependency]['type'] != 'checkbox')
|
|
{
|
|
BoolCheck = false;
|
|
}
|
|
}
|
|
|
|
// If one of the dependencies isn't a Boolean, skip the rest
|
|
if (BoolCheck == false)
|
|
{
|
|
console.error(`OIF | ${config} | ${key} | Dependency isn't a Boolean`);
|
|
continue;
|
|
}
|
|
|
|
// Check if the dependecies exist
|
|
let CurrentRequirements = [];
|
|
for (let index = 0; index < CurrentDependencies.length; index++)
|
|
{
|
|
// Check if the current dependency exists
|
|
const CurrentDependency = CurrentDependencies[index];
|
|
let CurrentRequirement = SettingsSkeleton.Settings[config][CurrentDependency];
|
|
if (CurrentRequirement != null && CurrentRequirement != undefined)
|
|
{
|
|
CurrentRequirements.push(CurrentRequirement);
|
|
}
|
|
}
|
|
|
|
// Check if all dependencies are met
|
|
for (let index = 0; index < CurrentRequirements.length; index++)
|
|
{
|
|
// Check if the current dependency is met
|
|
const CurrentRequirement = CurrentRequirements[index];
|
|
if (!SettingsSkeleton._protoGet(config, CurrentRequirement.referenceID))
|
|
{
|
|
AllRequirementsMet = false;
|
|
}
|
|
}
|
|
|
|
// If all dependencies are met, enable the setting
|
|
if (AllRequirementsMet)
|
|
{
|
|
CurrentSetting['disabled'] = false;
|
|
CurrentSetting['disabledMessage'] = '';
|
|
}
|
|
// If not, disable the setting
|
|
else
|
|
{
|
|
// Create a string with all dependencies, so the user knows which ones are missing
|
|
// separeted all dependencies with a comma, except the last one, which is separeted with 'and'
|
|
let DependenciesString = '';
|
|
for (let index = 0; index < CurrentRequirements.length; index++)
|
|
{
|
|
const CurrentRequirement = CurrentRequirements[index];
|
|
if (index == CurrentRequirements.length - 1)
|
|
{
|
|
DependenciesString += ` and "${game.i18n.localize(CurrentRequirement['name'])}"`;
|
|
}
|
|
else
|
|
{
|
|
DependenciesString += `"${game.i18n.localize(CurrentRequirement['name'])}", `;
|
|
}
|
|
}
|
|
|
|
// Disable the setting and add a message
|
|
CurrentSetting['disabled'] = true;
|
|
CurrentSetting['disabledMessage'] = game.i18n.localize('OIF.Settings.OptionRequired').replace("${option}", DependenciesString);
|
|
}
|
|
}
|
|
|
|
// Check if setting has undependencies
|
|
if (CurrentUndependencies != null && CurrentUndependencies != undefined && AllRequirementsMet)
|
|
{
|
|
// Loop through all undependencies to check if the are Boolean
|
|
let BoolCheck = true;
|
|
for (let index = 0; index < CurrentUndependencies.length; index++)
|
|
{
|
|
// Check if the current undependency is a Boolean
|
|
const CurrentUndependency = CurrentUndependencies[index];
|
|
if (SettingsSkeleton.Settings[config][CurrentUndependency]['type'] != 'checkbox')
|
|
{
|
|
BoolCheck = false;
|
|
}
|
|
}
|
|
|
|
// If one of the undependencies isn't a Boolean, skip the rest
|
|
if (BoolCheck == false)
|
|
{
|
|
console.error(`OIF | ${config} | ${key} | Undependency isn't a Boolean`);
|
|
continue;
|
|
}
|
|
|
|
// Check if the undependecies exist
|
|
let CurrentUnrequirements = [];
|
|
for (let index = 0; index < CurrentUndependencies.length; index++)
|
|
{
|
|
// Check if the current undependency exists
|
|
const CurrentUndependency = CurrentUndependencies[index];
|
|
let CurrentRequirement = SettingsSkeleton.Settings[config][CurrentUndependency];
|
|
if (CurrentRequirement != null && CurrentRequirement != undefined)
|
|
{
|
|
CurrentUnrequirements.push(CurrentRequirement);
|
|
}
|
|
}
|
|
|
|
// Check if all undependencies are met
|
|
let AllUnrequirementsMet = true;
|
|
for (let index = 0; index < CurrentUnrequirements.length; index++)
|
|
{
|
|
// Check if the current undependency is met
|
|
const CurrentUnrequirement = CurrentUnrequirements[index];
|
|
if (SettingsSkeleton._protoGet(config, CurrentUnrequirement.referenceID))
|
|
{
|
|
AllUnrequirementsMet = false;
|
|
}
|
|
}
|
|
|
|
// If all undependencies are met, enable the setting
|
|
if (AllUnrequirementsMet)
|
|
{
|
|
CurrentSetting['disabled'] = false;
|
|
CurrentSetting['disabledMessage'] = '';
|
|
}
|
|
// If not, disable the setting
|
|
else
|
|
{
|
|
// Create a string with all undependencies, so the user knows which ones are missing
|
|
// separeted all undependencies with a comma, except the last one, which is separeted with 'and'
|
|
let UndependenciesString = '';
|
|
for (let index = 0; index < CurrentUnrequirements.length; index++)
|
|
{
|
|
const CurrentUnrequirement = CurrentUnrequirements[index];
|
|
if (index == CurrentUnrequirements.length - 1)
|
|
{
|
|
UndependenciesString += ` and "${game.i18n.localize(CurrentUnrequirement['name'])}"`;
|
|
}
|
|
else
|
|
{
|
|
UndependenciesString += `"${game.i18n.localize(CurrentUnrequirement['name'])}", `;
|
|
}
|
|
}
|
|
|
|
// Disable the setting and add a message
|
|
CurrentSetting['disabled'] = true;
|
|
CurrentSetting['disabledMessage'] = game.i18n.localize('OIF.Settings.OptionUnrequired').replace("${option}", UndependenciesString);
|
|
}
|
|
}
|
|
}
|
|
|
|
Hooks.call(OIF.HOOKS.CHANGE_SETTINGS, SettingsSkeleton.Settings[config]);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Prototype function to get data for rendering .hbs
|
|
////////////////////////////////////////////////////////////
|
|
static _protoGetData(config, options)
|
|
{
|
|
// Only return settings that are in the 'world' scope
|
|
// for GMs
|
|
let ReturnedSettings = {};
|
|
for (const key in SettingsSkeleton.Settings[config])
|
|
{
|
|
let CurrentSetting = SettingsSkeleton.Settings[config][key];
|
|
if (CurrentSetting.scope == 'world')
|
|
{
|
|
if (game.user.isGM)
|
|
{
|
|
ReturnedSettings[key] = CurrentSetting;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ReturnedSettings[key] = CurrentSetting;
|
|
}
|
|
}
|
|
|
|
return { settings: ReturnedSettings }
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// Activate listeners
|
|
////////////////////////////////////////////////////////////
|
|
activateListeners(html, config)
|
|
{
|
|
super.activateListeners(html);
|
|
|
|
// Checkbox
|
|
html.on('change', 'input[type=checkbox]', this._handleCheckBoxInteraction.bind(this, config));
|
|
// Slider
|
|
html.on('change', 'input[type=range]', this._handleSliderInteraction.bind(this, config));
|
|
html.on('change', 'input[type=number]', this._handleSliderInteraction.bind(this, config));
|
|
// String
|
|
html.on('change', 'input[type=text]', this._handleStringInteraction.bind(this, config));
|
|
// Dropdown
|
|
html.on('change', 'select', this._handleDropdownInteraction.bind(this, config));
|
|
}
|
|
} |